diff --git a/typo3/sysext/compatibility6/Migrations/Code/ClassAliasMap.php b/typo3/sysext/compatibility6/Migrations/Code/ClassAliasMap.php
index e1e85551ccfea49a89c2b50e8e051dd8506e16f6..31e12cae333d9adf4bc05b089e46306709b49395 100644
--- a/typo3/sysext/compatibility6/Migrations/Code/ClassAliasMap.php
+++ b/typo3/sysext/compatibility6/Migrations/Code/ClassAliasMap.php
@@ -172,7 +172,8 @@ return array(
 	't3lib_refindex' => \TYPO3\CMS\Core\Database\ReferenceIndex::class,
 	't3lib_loadDBGroup' => \TYPO3\CMS\Core\Database\RelationHandler::class,
 	't3lib_softrefproc' => \TYPO3\CMS\Core\Database\SoftReferenceIndex::class,
-	't3lib_sqlparser' => \TYPO3\CMS\Core\Database\SqlParser::class,
+	't3lib_sqlparser' => \TYPO3\CMS\Dbal\Database\SqlParser::class,
+	'TYPO3\\CMS\\Core\\Database\\SqlParser' => \TYPO3\CMS\Dbal\Database\SqlParser::class,
 	't3lib_extTables_PostProcessingHook' => \TYPO3\CMS\Core\Database\TableConfigurationPostProcessingHookInterface::class,
 	't3lib_TCEmain' => \TYPO3\CMS\Core\DataHandling\DataHandler::class,
 	't3lib_TCEmain_checkModifyAccessListHook' => \TYPO3\CMS\Core\DataHandling\DataHandlerCheckModifyAccessListHookInterface::class,
diff --git a/typo3/sysext/core/Classes/Database/SqlParser.php b/typo3/sysext/core/Classes/Database/SqlParser.php
deleted file mode 100644
index 70e7dafb5b5dfc59576916be4ae1163d8475965d..0000000000000000000000000000000000000000
--- a/typo3/sysext/core/Classes/Database/SqlParser.php
+++ /dev/null
@@ -1,2100 +0,0 @@
-<?php
-namespace TYPO3\CMS\Core\Database;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * TYPO3 SQL parser class.
- */
-class SqlParser {
-
-	/**
-	 * Parsing error string
-	 *
-	 * @var string
-	 */
-	public $parse_error = '';
-
-	/**
-	 * Last stop keyword used.
-	 *
-	 * @var string
-	 */
-	public $lastStopKeyWord = '';
-
-	/**
-	 * Find "comparator"
-	 *
-	 * @var array
-	 */
-	static protected $comparatorPatterns = array(
-		'<=',
-		'>=',
-		'<>',
-		'<',
-		'>',
-		'=',
-		'!=',
-		'NOT[[:space:]]+IN',
-		'IN',
-		'NOT[[:space:]]+LIKE[[:space:]]+BINARY',
-		'LIKE[[:space:]]+BINARY',
-		'NOT[[:space:]]+LIKE',
-		'LIKE',
-		'IS[[:space:]]+NOT',
-		'IS',
-		'BETWEEN',
-		'NOT[[:space]]+BETWEEN'
-	);
-
-	/**
-	 * Whitespaces in a query
-	 *
-	 * @var array
-	 */
-	static protected $interQueryWhitespaces = array(' ', TAB, CR, LF);
-
-	/**
-	 * Default constructor
-	 */
-	public function __construct() {}
-
-	/*************************************
-	 *
-	 * SQL Parsing, full queries
-	 *
-	 **************************************/
-	/**
-	 * Parses any single SQL query
-	 *
-	 * @param string $parseString SQL query
-	 * @return array Result array with all the parts in - or error message string
-	 * @see compileSQL(), debug_testSQL()
-	 */
-	public function parseSQL($parseString) {
-		// Prepare variables:
-		$parseString = $this->trimSQL($parseString);
-		$this->parse_error = '';
-		$result = array();
-		// Finding starting keyword of string:
-		$_parseString = $parseString;
-		// Protecting original string...
-		$keyword = $this->nextPart($_parseString, '^(SELECT|UPDATE|INSERT[[:space:]]+INTO|DELETE[[:space:]]+FROM|EXPLAIN|(DROP|CREATE|ALTER|TRUNCATE)[[:space:]]+TABLE|CREATE[[:space:]]+DATABASE)[[:space:]]+');
-		$keyword = $this->normalizeKeyword($keyword);
-		switch ($keyword) {
-			case 'SELECT':
-				// Parsing SELECT query:
-				$result = $this->parseSELECT($parseString);
-				break;
-			case 'UPDATE':
-				// Parsing UPDATE query:
-				$result = $this->parseUPDATE($parseString);
-				break;
-			case 'INSERTINTO':
-				// Parsing INSERT query:
-				$result = $this->parseINSERT($parseString);
-				break;
-			case 'DELETEFROM':
-				// Parsing DELETE query:
-				$result = $this->parseDELETE($parseString);
-				break;
-			case 'EXPLAIN':
-				// Parsing EXPLAIN SELECT query:
-				$result = $this->parseEXPLAIN($parseString);
-				break;
-			case 'DROPTABLE':
-				// Parsing DROP TABLE query:
-				$result = $this->parseDROPTABLE($parseString);
-				break;
-			case 'ALTERTABLE':
-				// Parsing ALTER TABLE query:
-				$result = $this->parseALTERTABLE($parseString);
-				break;
-			case 'CREATETABLE':
-				// Parsing CREATE TABLE query:
-				$result = $this->parseCREATETABLE($parseString);
-				break;
-			case 'CREATEDATABASE':
-				// Parsing CREATE DATABASE query:
-				$result = $this->parseCREATEDATABASE($parseString);
-				break;
-			case 'TRUNCATETABLE':
-				// Parsing TRUNCATE TABLE query:
-				$result = $this->parseTRUNCATETABLE($parseString);
-				break;
-			default:
-				$result = $this->parseError('"' . $keyword . '" is not a keyword', $parseString);
-		}
-		return $result;
-	}
-
-	/**
-	 * Parsing SELECT query
-	 *
-	 * @param string $parseString SQL string with SELECT query to parse
-	 * @param array $parameterReferences Array holding references to either named (:name) or question mark (?) parameters found
-	 * @return mixed Returns array with components of SELECT query on success, otherwise an error message string.
-	 * @see compileSELECT()
-	 */
-	protected function parseSELECT($parseString, &$parameterReferences = NULL) {
-		// Removing SELECT:
-		$parseString = $this->trimSQL($parseString);
-		$parseString = ltrim(substr($parseString, 6));
-		// Init output variable:
-		$result = array();
-		if ($parameterReferences === NULL) {
-			$result['parameters'] = array();
-			$parameterReferences = &$result['parameters'];
-		}
-		$result['type'] = 'SELECT';
-		// Looking for STRAIGHT_JOIN keyword:
-		$result['STRAIGHT_JOIN'] = $this->nextPart($parseString, '^(STRAIGHT_JOIN)[[:space:]]+');
-		// Select fields:
-		$result['SELECT'] = $this->parseFieldList($parseString, '^(FROM)[[:space:]]+');
-		if ($this->parse_error) {
-			return $this->parse_error;
-		}
-		// Continue if string is not ended:
-		if ($parseString) {
-			// Get table list:
-			$result['FROM'] = $this->parseFromTables($parseString, '^(WHERE)[[:space:]]+');
-			if ($this->parse_error) {
-				return $this->parse_error;
-			}
-			// If there are more than just the tables (a WHERE clause that would be...)
-			if ($parseString) {
-				// Get WHERE clause:
-				$result['WHERE'] = $this->parseWhereClause($parseString, '^((GROUP|ORDER)[[:space:]]+BY|LIMIT)[[:space:]]+', $parameterReferences);
-				if ($this->parse_error) {
-					return $this->parse_error;
-				}
-				// If the WHERE clause parsing was stopped by GROUP BY, ORDER BY or LIMIT, then proceed with parsing:
-				if ($this->lastStopKeyWord) {
-					// GROUP BY parsing:
-					if ($this->lastStopKeyWord === 'GROUPBY') {
-						$result['GROUPBY'] = $this->parseFieldList($parseString, '^(ORDER[[:space:]]+BY|LIMIT)[[:space:]]+');
-						if ($this->parse_error) {
-							return $this->parse_error;
-						}
-					}
-					// ORDER BY parsing:
-					if ($this->lastStopKeyWord === 'ORDERBY') {
-						$result['ORDERBY'] = $this->parseFieldList($parseString, '^(LIMIT)[[:space:]]+');
-						if ($this->parse_error) {
-							return $this->parse_error;
-						}
-					}
-					// LIMIT parsing:
-					if ($this->lastStopKeyWord === 'LIMIT') {
-						if (preg_match('/^([0-9]+|[0-9]+[[:space:]]*,[[:space:]]*[0-9]+)$/', trim($parseString))) {
-							$result['LIMIT'] = $parseString;
-						} else {
-							return $this->parseError('No value for limit!', $parseString);
-						}
-					}
-				}
-			}
-		} else {
-			return $this->parseError('No table to select from!', $parseString);
-		}
-		// Store current parseString in the result array for possible further processing (e.g., subquery support by DBAL)
-		$result['parseString'] = $parseString;
-		// Return result:
-		return $result;
-	}
-
-	/**
-	 * Parsing UPDATE query
-	 *
-	 * @param string $parseString SQL string with UPDATE query to parse
-	 * @return mixed Returns array with components of UPDATE query on success, otherwise an error message string.
-	 * @see compileUPDATE()
-	 */
-	protected function parseUPDATE($parseString) {
-		// Removing UPDATE
-		$parseString = $this->trimSQL($parseString);
-		$parseString = ltrim(substr($parseString, 6));
-		// Init output variable:
-		$result = array();
-		$result['type'] = 'UPDATE';
-		// Get table:
-		$result['TABLE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+');
-		// Continue if string is not ended:
-		if ($result['TABLE']) {
-			if ($parseString && $this->nextPart($parseString, '^(SET)[[:space:]]+')) {
-				$comma = TRUE;
-				// Get field/value pairs:
-				while ($comma) {
-					if ($fieldName = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]*=')) {
-						// Strip off "=" sign.
-						$this->nextPart($parseString, '^(=)');
-						$value = $this->getValue($parseString);
-						$result['FIELDS'][$fieldName] = $value;
-					} else {
-						return $this->parseError('No fieldname found', $parseString);
-					}
-					$comma = $this->nextPart($parseString, '^(,)');
-				}
-				// WHERE
-				if ($this->nextPart($parseString, '^(WHERE)')) {
-					$result['WHERE'] = $this->parseWhereClause($parseString);
-					if ($this->parse_error) {
-						return $this->parse_error;
-					}
-				}
-			} else {
-				return $this->parseError('Query missing SET...', $parseString);
-			}
-		} else {
-			return $this->parseError('No table found!', $parseString);
-		}
-		// Should be no more content now:
-		if ($parseString) {
-			return $this->parseError('Still content in clause after parsing!', $parseString);
-		}
-		// Return result:
-		return $result;
-	}
-
-	/**
-	 * Parsing INSERT query
-	 *
-	 * @param string $parseString SQL string with INSERT query to parse
-	 * @return mixed Returns array with components of INSERT query on success, otherwise an error message string.
-	 * @see compileINSERT()
-	 */
-	protected function parseINSERT($parseString) {
-		// Removing INSERT
-		$parseString = $this->trimSQL($parseString);
-		$parseString = ltrim(substr(ltrim(substr($parseString, 6)), 4));
-		// Init output variable:
-		$result = array();
-		$result['type'] = 'INSERT';
-		// Get table:
-		$result['TABLE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)([[:space:]]+|\\()');
-		if ($result['TABLE']) {
-			// In this case there are no field names mentioned in the SQL!
-			if ($this->nextPart($parseString, '^(VALUES)([[:space:]]+|\\()')) {
-				// Get values/fieldnames (depending...)
-				$result['VALUES_ONLY'] = $this->getValue($parseString, 'IN');
-				if ($this->parse_error) {
-					return $this->parse_error;
-				}
-				if (preg_match('/^,/', $parseString)) {
-					$result['VALUES_ONLY'] = array($result['VALUES_ONLY']);
-					$result['EXTENDED'] = '1';
-					while ($this->nextPart($parseString, '^(,)') === ',') {
-						$result['VALUES_ONLY'][] = $this->getValue($parseString, 'IN');
-						if ($this->parse_error) {
-							return $this->parse_error;
-						}
-					}
-				}
-			} else {
-				// There are apparently fieldnames listed:
-				$fieldNames = $this->getValue($parseString, '_LIST');
-				if ($this->parse_error) {
-					return $this->parse_error;
-				}
-				// "VALUES" keyword binds the fieldnames to values:
-				if ($this->nextPart($parseString, '^(VALUES)([[:space:]]+|\\()')) {
-					$result['FIELDS'] = array();
-					do {
-						// Using the "getValue" function to get the field list...
-						$values = $this->getValue($parseString, 'IN');
-						if ($this->parse_error) {
-							return $this->parse_error;
-						}
-						$insertValues = array();
-						foreach ($fieldNames as $k => $fN) {
-							if (preg_match('/^[[:alnum:]_]+$/', $fN)) {
-								if (isset($values[$k])) {
-									if (!isset($insertValues[$fN])) {
-										$insertValues[$fN] = $values[$k];
-									} else {
-										return $this->parseError('Fieldname ("' . $fN . '") already found in list!', $parseString);
-									}
-								} else {
-									return $this->parseError('No value set!', $parseString);
-								}
-							} else {
-								return $this->parseError('Invalid fieldname ("' . $fN . '")', $parseString);
-							}
-						}
-						if (isset($values[$k + 1])) {
-							return $this->parseError('Too many values in list!', $parseString);
-						}
-						$result['FIELDS'][] = $insertValues;
-					} while ($this->nextPart($parseString, '^(,)') === ',');
-					if (count($result['FIELDS']) === 1) {
-						$result['FIELDS'] = $result['FIELDS'][0];
-					} else {
-						$result['EXTENDED'] = '1';
-					}
-				} else {
-					return $this->parseError('VALUES keyword expected', $parseString);
-				}
-			}
-		} else {
-			return $this->parseError('No table found!', $parseString);
-		}
-		// Should be no more content now:
-		if ($parseString) {
-			return $this->parseError('Still content after parsing!', $parseString);
-		}
-		// Return result
-		return $result;
-	}
-
-	/**
-	 * Parsing DELETE query
-	 *
-	 * @param string $parseString SQL string with DELETE query to parse
-	 * @return mixed Returns array with components of DELETE query on success, otherwise an error message string.
-	 * @see compileDELETE()
-	 */
-	protected function parseDELETE($parseString) {
-		// Removing DELETE
-		$parseString = $this->trimSQL($parseString);
-		$parseString = ltrim(substr(ltrim(substr($parseString, 6)), 4));
-		// Init output variable:
-		$result = array();
-		$result['type'] = 'DELETE';
-		// Get table:
-		$result['TABLE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+');
-		if ($result['TABLE']) {
-			// WHERE
-			if ($this->nextPart($parseString, '^(WHERE)')) {
-				$result['WHERE'] = $this->parseWhereClause($parseString);
-				if ($this->parse_error) {
-					return $this->parse_error;
-				}
-			}
-		} else {
-			return $this->parseError('No table found!', $parseString);
-		}
-		// Should be no more content now:
-		if ($parseString) {
-			return $this->parseError('Still content in clause after parsing!', $parseString);
-		}
-		// Return result:
-		return $result;
-	}
-
-	/**
-	 * Parsing EXPLAIN query
-	 *
-	 * @param string $parseString SQL string with EXPLAIN query to parse
-	 * @return mixed Returns array with components of EXPLAIN query on success, otherwise an error message string.
-	 * @see parseSELECT()
-	 */
-	protected function parseEXPLAIN($parseString) {
-		// Removing EXPLAIN
-		$parseString = $this->trimSQL($parseString);
-		$parseString = ltrim(substr($parseString, 6));
-		// Init output variable:
-		$result = $this->parseSELECT($parseString);
-		if (is_array($result)) {
-			$result['type'] = 'EXPLAIN';
-		}
-		return $result;
-	}
-
-	/**
-	 * Parsing CREATE TABLE query
-	 *
-	 * @param string $parseString SQL string starting with CREATE TABLE
-	 * @return mixed Returns array with components of CREATE TABLE query on success, otherwise an error message string.
-	 * @see compileCREATETABLE()
-	 */
-	protected function parseCREATETABLE($parseString) {
-		// Removing CREATE TABLE
-		$parseString = $this->trimSQL($parseString);
-		$parseString = ltrim(substr(ltrim(substr($parseString, 6)), 5));
-		// Init output variable:
-		$result = array();
-		$result['type'] = 'CREATETABLE';
-		// Get table:
-		$result['TABLE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]*\\(', TRUE);
-		if ($result['TABLE']) {
-			// While the parseString is not yet empty:
-			while ($parseString !== '') {
-				// Getting key
-				if ($key = $this->nextPart($parseString, '^(KEY|PRIMARY KEY|UNIQUE KEY|UNIQUE)([[:space:]]+|\\()')) {
-					$key = $this->normalizeKeyword($key);
-					switch ($key) {
-						case 'PRIMARYKEY':
-							$result['KEYS']['PRIMARYKEY'] = $this->getValue($parseString, '_LIST');
-							if ($this->parse_error) {
-								return $this->parse_error;
-							}
-							break;
-						case 'UNIQUE':
-
-						case 'UNIQUEKEY':
-							if ($keyName = $this->nextPart($parseString, '^([[:alnum:]_]+)([[:space:]]+|\\()')) {
-								$result['KEYS']['UNIQUE'] = array($keyName => $this->getValue($parseString, '_LIST'));
-								if ($this->parse_error) {
-									return $this->parse_error;
-								}
-							} else {
-								return $this->parseError('No keyname found', $parseString);
-							}
-							break;
-						case 'KEY':
-							if ($keyName = $this->nextPart($parseString, '^([[:alnum:]_]+)([[:space:]]+|\\()')) {
-								$result['KEYS'][$keyName] = $this->getValue($parseString, '_LIST', 'INDEX');
-								if ($this->parse_error) {
-									return $this->parse_error;
-								}
-							} else {
-								return $this->parseError('No keyname found', $parseString);
-							}
-							break;
-					}
-				} elseif ($fieldName = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+')) {
-					// Getting field:
-					$result['FIELDS'][$fieldName]['definition'] = $this->parseFieldDef($parseString);
-					if ($this->parse_error) {
-						return $this->parse_error;
-					}
-				}
-				// Finding delimiter:
-				$delim = $this->nextPart($parseString, '^(,|\\))');
-				if (!$delim) {
-					return $this->parseError('No delimiter found', $parseString);
-				} elseif ($delim === ')') {
-					break;
-				}
-			}
-			// Finding what is after the table definition - table type in MySQL
-			if ($delim === ')') {
-				if ($this->nextPart($parseString, '^((ENGINE|TYPE)[[:space:]]*=)')) {
-					$result['engine'] = $parseString;
-					$parseString = '';
-				}
-			} else {
-				return $this->parseError('No fieldname found!', $parseString);
-			}
-		} else {
-			return $this->parseError('No table found!', $parseString);
-		}
-		// Should be no more content now:
-		if ($parseString) {
-			return $this->parseError('Still content in clause after parsing!', $parseString);
-		}
-		return $result;
-	}
-
-	/**
-	 * Parsing ALTER TABLE query
-	 *
-	 * @param string $parseString SQL string starting with ALTER TABLE
-	 * @return mixed Returns array with components of ALTER TABLE query on success, otherwise an error message string.
-	 * @see compileALTERTABLE()
-	 */
-	protected function parseALTERTABLE($parseString) {
-		// Removing ALTER TABLE
-		$parseString = $this->trimSQL($parseString);
-		$parseString = ltrim(substr(ltrim(substr($parseString, 5)), 5));
-		// Init output variable:
-		$result = array();
-		$result['type'] = 'ALTERTABLE';
-		// Get table:
-		$hasBackquote = $this->nextPart($parseString, '^(`)') === '`';
-		$result['TABLE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)' . ($hasBackquote ? '`' : '') . '[[:space:]]+');
-		if ($hasBackquote && $this->nextPart($parseString, '^(`)') !== '`') {
-			return $this->parseError('No end backquote found!', $parseString);
-		}
-		if ($result['TABLE']) {
-			if ($result['action'] = $this->nextPart($parseString, '^(CHANGE|DROP[[:space:]]+KEY|DROP[[:space:]]+PRIMARY[[:space:]]+KEY|ADD[[:space:]]+KEY|ADD[[:space:]]+PRIMARY[[:space:]]+KEY|ADD[[:space:]]+UNIQUE|DROP|ADD|RENAME|DEFAULT[[:space:]]+CHARACTER[[:space:]]+SET|ENGINE)([[:space:]]+|\\(|=)')) {
-				$actionKey = $this->normalizeKeyword($result['action']);
-				// Getting field:
-				if (\TYPO3\CMS\Core\Utility\GeneralUtility::inList('ADDPRIMARYKEY,DROPPRIMARYKEY,ENGINE', $actionKey) || ($fieldKey = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+'))) {
-					switch ($actionKey) {
-						case 'ADD':
-							$result['FIELD'] = $fieldKey;
-							$result['definition'] = $this->parseFieldDef($parseString);
-							if ($this->parse_error) {
-								return $this->parse_error;
-							}
-							break;
-						case 'DROP':
-						case 'RENAME':
-							$result['FIELD'] = $fieldKey;
-							break;
-						case 'CHANGE':
-							$result['FIELD'] = $fieldKey;
-							if ($result['newField'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+')) {
-								$result['definition'] = $this->parseFieldDef($parseString);
-								if ($this->parse_error) {
-									return $this->parse_error;
-								}
-							} else {
-								return $this->parseError('No NEW field name found', $parseString);
-							}
-							break;
-						case 'ADDKEY':
-						case 'ADDPRIMARYKEY':
-						case 'ADDUNIQUE':
-							$result['KEY'] = $fieldKey;
-							$result['fields'] = $this->getValue($parseString, '_LIST', 'INDEX');
-							if ($this->parse_error) {
-								return $this->parse_error;
-							}
-							break;
-						case 'DROPKEY':
-							$result['KEY'] = $fieldKey;
-							break;
-						case 'DROPPRIMARYKEY':
-							// @todo ???
-							break;
-						case 'DEFAULTCHARACTERSET':
-							$result['charset'] = $fieldKey;
-							break;
-						case 'ENGINE':
-							$result['engine'] = $this->nextPart($parseString, '^=[[:space:]]*([[:alnum:]]+)[[:space:]]+', TRUE);
-							break;
-					}
-				} else {
-					return $this->parseError('No field name found', $parseString);
-				}
-			} else {
-				return $this->parseError('No action CHANGE, DROP or ADD found!', $parseString);
-			}
-		} else {
-			return $this->parseError('No table found!', $parseString);
-		}
-		// Should be no more content now:
-		if ($parseString) {
-			return $this->parseError('Still content in clause after parsing!', $parseString);
-		}
-		return $result;
-	}
-
-	/**
-	 * Parsing DROP TABLE query
-	 *
-	 * @param string $parseString SQL string starting with DROP TABLE
-	 * @return mixed Returns array with components of DROP TABLE query on success, otherwise an error message string.
-	 */
-	protected function parseDROPTABLE($parseString) {
-		// Removing DROP TABLE
-		$parseString = $this->trimSQL($parseString);
-		$parseString = ltrim(substr(ltrim(substr($parseString, 4)), 5));
-		// Init output variable:
-		$result = array();
-		$result['type'] = 'DROPTABLE';
-		// IF EXISTS
-		$result['ifExists'] = $this->nextPart($parseString, '^(IF[[:space:]]+EXISTS[[:space:]]+)');
-		// Get table:
-		$result['TABLE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+');
-		if ($result['TABLE']) {
-			// Should be no more content now:
-			if ($parseString) {
-				return $this->parseError('Still content in clause after parsing!', $parseString);
-			}
-			return $result;
-		} else {
-			return $this->parseError('No table found!', $parseString);
-		}
-	}
-
-	/**
-	 * Parsing CREATE DATABASE query
-	 *
-	 * @param string $parseString SQL string starting with CREATE DATABASE
-	 * @return mixed Returns array with components of CREATE DATABASE query on success, otherwise an error message string.
-	 */
-	protected function parseCREATEDATABASE($parseString) {
-		// Removing CREATE DATABASE
-		$parseString = $this->trimSQL($parseString);
-		$parseString = ltrim(substr(ltrim(substr($parseString, 6)), 8));
-		// Init output variable:
-		$result = array();
-		$result['type'] = 'CREATEDATABASE';
-		// Get table:
-		$result['DATABASE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+');
-		if ($result['DATABASE']) {
-			// Should be no more content now:
-			if ($parseString) {
-				return $this->parseError('Still content in clause after parsing!', $parseString);
-			}
-			return $result;
-		} else {
-			return $this->parseError('No database found!', $parseString);
-		}
-	}
-
-	/**
-	 * Parsing TRUNCATE TABLE query
-	 *
-	 * @param string $parseString SQL string starting with TRUNCATE TABLE
-	 * @return mixed Returns array with components of TRUNCATE TABLE query on success, otherwise an error message string.
-	 */
-	protected function parseTRUNCATETABLE($parseString) {
-		// Removing TRUNCATE TABLE
-		$parseString = $this->trimSQL($parseString);
-		$parseString = ltrim(substr(ltrim(substr($parseString, 8)), 5));
-		// Init output variable:
-		$result = array();
-		$result['type'] = 'TRUNCATETABLE';
-		// Get table:
-		$result['TABLE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+');
-		if ($result['TABLE']) {
-			// Should be no more content now:
-			if ($parseString) {
-				return $this->parseError('Still content in clause after parsing!', $parseString);
-			}
-			return $result;
-		} else {
-			return $this->parseError('No table found!', $parseString);
-		}
-	}
-
-	/**************************************
-	 *
-	 * SQL Parsing, helper functions for parts of queries
-	 *
-	 **************************************/
-	/**
-	 * Parsing the fields in the "SELECT [$selectFields] FROM" part of a query into an array.
-	 * The output from this function can be compiled back into a field list with ->compileFieldList()
-	 * Will detect the keywords "DESC" and "ASC" after the table name; thus is can be used for parsing the more simply ORDER BY and GROUP BY field lists as well!
-	 *
-	 * @param string $parseString The string with fieldnames, eg. "title, uid AS myUid, max(tstamp), count(*)" etc. NOTICE: passed by reference!
-	 * @param string $stopRegex Regular expressing to STOP parsing, eg. '^(FROM)([[:space:]]*)'
-	 * @return array If successful parsing, returns an array, otherwise an error string.
-	 * @see compileFieldList()
-	 */
-	public function parseFieldList(&$parseString, $stopRegex = '') {
-		$stack = array();
-		// Contains the parsed content
-		if ($parseString === '') {
-			return $stack;
-		}
-		// @todo - should never happen, why does it?
-		// Pointer to positions in $stack
-		$pnt = 0;
-		// Indicates the parenthesis level we are at.
-		$level = 0;
-		// Recursivity brake.
-		$loopExit = 0;
-		// Prepare variables:
-		$parseString = $this->trimSQL($parseString);
-		$this->lastStopKeyWord = '';
-		$this->parse_error = '';
-		// Parse any SQL hint / comments
-		$stack[$pnt]['comments'] = $this->nextPart($parseString, '^(\\/\\*.*\\*\\/)');
-		// $parseString is continuously shortened by the process and we keep parsing it till it is zero:
-		while ($parseString !== '') {
-			// Checking if we are inside / outside parenthesis (in case of a function like count(), max(), min() etc...):
-			// Inside parenthesis here (does NOT detect if values in quotes are used, the only token is ")" or "("):
-			if ($level > 0) {
-				// Accumulate function content until next () parenthesis:
-				$funcContent = $this->nextPart($parseString, '^([^()]*.)');
-				$stack[$pnt]['func_content.'][] = array(
-					'level' => $level,
-					'func_content' => substr($funcContent, 0, -1)
-				);
-				$stack[$pnt]['func_content'] .= $funcContent;
-				// Detecting ( or )
-				switch (substr($stack[$pnt]['func_content'], -1)) {
-					case '(':
-						$level++;
-						break;
-					case ')':
-						$level--;
-						// If this was the last parenthesis:
-						if (!$level) {
-							$stack[$pnt]['func_content'] = substr($stack[$pnt]['func_content'], 0, -1);
-							// Remove any whitespace after the parenthesis.
-							$parseString = ltrim($parseString);
-						}
-						break;
-				}
-			} else {
-				// Outside parenthesis, looking for next field:
-				// Looking for a flow-control construct (only known constructs supported)
-				if (preg_match('/^case([[:space:]][[:alnum:]\\*._]+)?[[:space:]]when/i', $parseString)) {
-					$stack[$pnt]['type'] = 'flow-control';
-					$stack[$pnt]['flow-control'] = $this->parseCaseStatement($parseString);
-					// Looking for "AS" alias:
-					if ($as = $this->nextPart($parseString, '^(AS)[[:space:]]+')) {
-						$stack[$pnt]['as'] = $this->nextPart($parseString, '^([[:alnum:]_]+)(,|[[:space:]]+)');
-						$stack[$pnt]['as_keyword'] = $as;
-					}
-				} else {
-					// Looking for a known function (only known functions supported)
-					$func = $this->nextPart($parseString, '^(count|max|min|floor|sum|avg)[[:space:]]*\\(');
-					if ($func) {
-						// Strip off "("
-						$parseString = trim(substr($parseString, 1));
-						$stack[$pnt]['type'] = 'function';
-						$stack[$pnt]['function'] = $func;
-						// increse parenthesis level counter.
-						$level++;
-					} else {
-						$stack[$pnt]['distinct'] = $this->nextPart($parseString, '^(distinct[[:space:]]+)');
-						// Otherwise, look for regular fieldname:
-						if (($fieldName = $this->nextPart($parseString, '^([[:alnum:]\\*._]+)(,|[[:space:]]+)')) !== '') {
-							$stack[$pnt]['type'] = 'field';
-							// Explode fieldname into field and table:
-							$tableField = explode('.', $fieldName, 2);
-							if (count($tableField) === 2) {
-								$stack[$pnt]['table'] = $tableField[0];
-								$stack[$pnt]['field'] = $tableField[1];
-							} else {
-								$stack[$pnt]['table'] = '';
-								$stack[$pnt]['field'] = $tableField[0];
-							}
-						} else {
-							return $this->parseError('No field name found as expected in parseFieldList()', $parseString);
-						}
-					}
-				}
-			}
-			// After a function or field we look for "AS" alias and a comma to separate to the next field in the list:
-			if (!$level) {
-				// Looking for "AS" alias:
-				if ($as = $this->nextPart($parseString, '^(AS)[[:space:]]+')) {
-					$stack[$pnt]['as'] = $this->nextPart($parseString, '^([[:alnum:]_]+)(,|[[:space:]]+)');
-					$stack[$pnt]['as_keyword'] = $as;
-				}
-				// Looking for "ASC" or "DESC" keywords (for ORDER BY)
-				if ($sDir = $this->nextPart($parseString, '^(ASC|DESC)([[:space:]]+|,)')) {
-					$stack[$pnt]['sortDir'] = $sDir;
-				}
-				// Looking for stop-keywords:
-				if ($stopRegex && ($this->lastStopKeyWord = $this->nextPart($parseString, $stopRegex))) {
-					$this->lastStopKeyWord = $this->normalizeKeyword($this->lastStopKeyWord);
-					return $stack;
-				}
-				// Looking for comma (since the stop-keyword did not trigger a return...)
-				if ($parseString !== '' && !$this->nextPart($parseString, '^(,)')) {
-					return $this->parseError('No comma found as expected in parseFieldList()', $parseString);
-				}
-				// Increasing pointer:
-				$pnt++;
-			}
-			// Check recursivity brake:
-			$loopExit++;
-			if ($loopExit > 500) {
-				return $this->parseError('More than 500 loops, exiting prematurely in parseFieldList()...', $parseString);
-			}
-		}
-		// Return result array:
-		return $stack;
-	}
-
-	/**
-	 * Parsing a CASE ... WHEN flow-control construct.
-	 * The output from this function can be compiled back with ->compileCaseStatement()
-	 *
-	 * @param string $parseString The string with the CASE ... WHEN construct, eg. "CASE field WHEN 1 THEN 0 ELSE ..." etc. NOTICE: passed by reference!
-	 * @return array If successful parsing, returns an array, otherwise an error string.
-	 * @see compileCaseConstruct()
-	 */
-	protected function parseCaseStatement(&$parseString) {
-		$result = array();
-		$result['type'] = $this->nextPart($parseString, '^(case)[[:space:]]+');
-		if (!preg_match('/^when[[:space:]]+/i', $parseString)) {
-			$value = $this->getValue($parseString);
-			if (!(isset($value[1]) || is_numeric($value[0]))) {
-				$result['case_field'] = $value[0];
-			} else {
-				$result['case_value'] = $value;
-			}
-		}
-		$result['when'] = array();
-		while ($this->nextPart($parseString, '^(when)[[:space:]]')) {
-			$when = array();
-			$when['when_value'] = $this->parseWhereClause($parseString, '^(then)[[:space:]]+');
-			$when['then_value'] = $this->getValue($parseString);
-			$result['when'][] = $when;
-		}
-		if ($this->nextPart($parseString, '^(else)[[:space:]]+')) {
-			$result['else'] = $this->getValue($parseString);
-		}
-		if (!$this->nextPart($parseString, '^(end)[[:space:]]+')) {
-			return $this->parseError('No "end" keyword found as expected in parseCaseStatement()', $parseString);
-		}
-		return $result;
-	}
-
-	/**
-	 * Parsing a CAST definition in the "JOIN [$parseString] ..." part of a query into an array.
-	 * The success of this parsing determines if that part of the query is supported by TYPO3.
-	 *
-	 * @param string $parseString JOIN clause to parse. NOTICE: passed by reference!
-	 * @return mixed If successful parsing, returns an array, otherwise an error string.
-	 */
-	protected function parseCastStatement(&$parseString) {
-		$this->nextPart($parseString, '^(CAST)[[:space:]]*');
-		$parseString = trim(substr($parseString, 1));
-		$castDefinition = array('type' => 'cast');
-		// Strip off "("
-		if ($fieldName = $this->nextPart($parseString, '^([[:alnum:]\\*._]+)[[:space:]]*')) {
-			// Parse field name into field and table:
-			$tableField = explode('.', $fieldName, 2);
-			if (count($tableField) === 2) {
-				$castDefinition['table'] = $tableField[0];
-				$castDefinition['field'] = $tableField[1];
-			} else {
-				$castDefinition['table'] = '';
-				$castDefinition['field'] = $tableField[0];
-			}
-		} else {
-			return $this->parseError('No casted join field found in parseCastStatement()!', $parseString);
-		}
-		if ($this->nextPart($parseString, '^([[:space:]]*AS[[:space:]]*)')) {
-			$castDefinition['datatype'] = $this->getValue($parseString);
-		}
-		if (!$this->nextPart($parseString, '^([)])')) {
-			return $this->parseError('No end parenthesis at end of CAST function', $parseString);
-		}
-		return $castDefinition;
-	}
-
-	/**
-	 * Parsing the tablenames in the "FROM [$parseString] WHERE" part of a query into an array.
-	 * The success of this parsing determines if that part of the query is supported by TYPO3.
-	 *
-	 * @param string $parseString List of tables, eg. "pages, tt_content" or "pages A, pages B". NOTICE: passed by reference!
-	 * @param string $stopRegex Regular expressing to STOP parsing, eg. '^(WHERE)([[:space:]]*)'
-	 * @return array If successful parsing, returns an array, otherwise an error string.
-	 * @see compileFromTables()
-	 */
-	public function parseFromTables(&$parseString, $stopRegex = '') {
-		// Prepare variables:
-		$parseString = $this->trimSQL($parseString);
-		$this->lastStopKeyWord = '';
-		$this->parse_error = '';
-		// Contains the parsed content
-		$stack = array();
-		// Pointer to positions in $stack
-		$pnt = 0;
-		// Recursivity brake.
-		$loopExit = 0;
-		// $parseString is continously shortend by the process and we keep parsing it till it is zero:
-		while ($parseString !== '') {
-			// Looking for the table:
-			if ($stack[$pnt]['table'] = $this->nextPart($parseString, '^([[:alnum:]_]+)(,|[[:space:]]+)')) {
-				// Looking for stop-keywords before fetching potential table alias:
-				if ($stopRegex && ($this->lastStopKeyWord = $this->nextPart($parseString, $stopRegex))) {
-					$this->lastStopKeyWord = $this->normalizeKeyword($this->lastStopKeyWord);
-					return $stack;
-				}
-				if (!preg_match('/^(LEFT|RIGHT|JOIN|INNER)[[:space:]]+/i', $parseString)) {
-					$stack[$pnt]['as_keyword'] = $this->nextPart($parseString, '^(AS[[:space:]]+)');
-					$stack[$pnt]['as'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]*');
-				}
-			} else {
-				return $this->parseError('No table name found as expected in parseFromTables()!', $parseString);
-			}
-			// Looking for JOIN
-			$joinCnt = 0;
-			while ($join = $this->nextPart($parseString, '^(((INNER|(LEFT|RIGHT)([[:space:]]+OUTER)?)[[:space:]]+)?JOIN)[[:space:]]+')) {
-				$stack[$pnt]['JOIN'][$joinCnt]['type'] = $join;
-				if ($stack[$pnt]['JOIN'][$joinCnt]['withTable'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+', 1)) {
-					if (!preg_match('/^ON[[:space:]]+/i', $parseString)) {
-						$stack[$pnt]['JOIN'][$joinCnt]['as_keyword'] = $this->nextPart($parseString, '^(AS[[:space:]]+)');
-						$stack[$pnt]['JOIN'][$joinCnt]['as'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+');
-					}
-					if (!$this->nextPart($parseString, '^(ON[[:space:]]+)')) {
-						return $this->parseError('No join condition found in parseFromTables()!', $parseString);
-					}
-					$stack[$pnt]['JOIN'][$joinCnt]['ON'] = array();
-					$condition = array('operator' => '');
-					$parseCondition = TRUE;
-					while ($parseCondition) {
-						if (($fieldName = $this->nextPart($parseString, '^([[:alnum:]._]+)[[:space:]]*(<=|>=|<|>|=|!=)')) !== '') {
-							// Parse field name into field and table:
-							$tableField = explode('.', $fieldName, 2);
-							$condition['left'] = array();
-							if (count($tableField) === 2) {
-								$condition['left']['table'] = $tableField[0];
-								$condition['left']['field'] = $tableField[1];
-							} else {
-								$condition['left']['table'] = '';
-								$condition['left']['field'] = $tableField[0];
-							}
-						} elseif (preg_match('/^CAST[[:space:]]*[(]/i', $parseString)) {
-							$condition['left'] = $this->parseCastStatement($parseString);
-							// Return the parse error
-							if (!is_array($condition['left'])) {
-								return $condition['left'];
-							}
-						} else {
-							return $this->parseError('No join field found in parseFromTables()!', $parseString);
-						}
-						// Find "comparator":
-						$condition['comparator'] = $this->nextPart($parseString, '^(<=|>=|<|>|=|!=)');
-						if (preg_match('/^CAST[[:space:]]*[(]/i', $parseString)) {
-							$condition['right'] = $this->parseCastStatement($parseString);
-							// Return the parse error
-							if (!is_array($condition['right'])) {
-								return $condition['right'];
-							}
-						} elseif (($fieldName = $this->nextPart($parseString, '^([[:alnum:]._]+)')) !== '') {
-							// Parse field name into field and table:
-							$tableField = explode('.', $fieldName, 2);
-							$condition['right'] = array();
-							if (count($tableField) === 2) {
-								$condition['right']['table'] = $tableField[0];
-								$condition['right']['field'] = $tableField[1];
-							} else {
-								$condition['right']['table'] = '';
-								$condition['right']['field'] = $tableField[0];
-							}
-						} elseif ($value = $this->getValue($parseString)) {
-							$condition['right']['value'] = $value;
-						} else {
-							return $this->parseError('No join field found in parseFromTables()!', $parseString);
-						}
-						$stack[$pnt]['JOIN'][$joinCnt]['ON'][] = $condition;
-						if (($operator = $this->nextPart($parseString, '^(AND|OR)')) !== '') {
-							$condition = array('operator' => $operator);
-						} else {
-							$parseCondition = FALSE;
-						}
-					}
-					$joinCnt++;
-				} else {
-					return $this->parseError('No join table found in parseFromTables()!', $parseString);
-				}
-			}
-			// Looking for stop-keywords:
-			if ($stopRegex && ($this->lastStopKeyWord = $this->nextPart($parseString, $stopRegex))) {
-				$this->lastStopKeyWord = $this->normalizeKeyword($this->lastStopKeyWord);
-				return $stack;
-			}
-			// Looking for comma:
-			if ($parseString !== '' && !$this->nextPart($parseString, '^(,)')) {
-				return $this->parseError('No comma found as expected in parseFromTables()', $parseString);
-			}
-			// Increasing pointer:
-			$pnt++;
-			// Check recursivity brake:
-			$loopExit++;
-			if ($loopExit > 500) {
-				return $this->parseError('More than 500 loops, exiting prematurely in parseFromTables()...', $parseString);
-			}
-		}
-		// Return result array:
-		return $stack;
-	}
-
-	/**
-	 * Parsing the WHERE clause fields in the "WHERE [$parseString] ..." part of a query into a multidimensional array.
-	 * The success of this parsing determines if that part of the query is supported by TYPO3.
-	 *
-	 * @param string $parseString WHERE clause to parse. NOTICE: passed by reference!
-	 * @param string $stopRegex Regular expressing to STOP parsing, eg. '^(GROUP BY|ORDER BY|LIMIT)([[:space:]]*)'
-	 * @param array $parameterReferences Array holding references to either named (:name) or question mark (?) parameters found
-	 * @return mixed If successful parsing, returns an array, otherwise an error string.
-	 */
-	public function parseWhereClause(&$parseString, $stopRegex = '', array &$parameterReferences = array()) {
-		// Prepare variables:
-		$parseString = $this->trimSQL($parseString);
-		$this->lastStopKeyWord = '';
-		$this->parse_error = '';
-		// Contains the parsed content
-		$stack = array(0 => array());
-		// Pointer to positions in $stack
-		$pnt = array(0 => 0);
-		// Determines parenthesis level
-		$level = 0;
-		// Recursivity brake.
-		$loopExit = 0;
-		// $parseString is continuously shortened by the process and we keep parsing it till it is zero:
-		while ($parseString !== '') {
-			// Look for next parenthesis level:
-			$newLevel = $this->nextPart($parseString, '^([(])');
-			// If new level is started, manage stack/pointers:
-			if ($newLevel === '(') {
-				// Increase level
-				$level++;
-				// Reset pointer for this level
-				$pnt[$level] = 0;
-				// Reset stack for this level
-				$stack[$level] = array();
-			} else {
-				// If no new level is started, just parse the current level:
-				// Find "modifier", eg. "NOT or !"
-				$stack[$level][$pnt[$level]]['modifier'] = trim($this->nextPart($parseString, '^(!|NOT[[:space:]]+)'));
-				// See if condition is EXISTS with a subquery
-				if (preg_match('/^EXISTS[[:space:]]*[(]/i', $parseString)) {
-					$stack[$level][$pnt[$level]]['func']['type'] = $this->nextPart($parseString, '^(EXISTS)[[:space:]]*');
-					// Strip off "("
-					$parseString = trim(substr($parseString, 1));
-					$stack[$level][$pnt[$level]]['func']['subquery'] = $this->parseSELECT($parseString, $parameterReferences);
-					// Seek to new position in parseString after parsing of the subquery
-					$parseString = $stack[$level][$pnt[$level]]['func']['subquery']['parseString'];
-					unset($stack[$level][$pnt[$level]]['func']['subquery']['parseString']);
-					if (!$this->nextPart($parseString, '^([)])')) {
-						return 'No ) parenthesis at end of subquery';
-					}
-				} else {
-					// See if LOCATE function is found
-					if (preg_match('/^LOCATE[[:space:]]*[(]/i', $parseString)) {
-						$stack[$level][$pnt[$level]]['func']['type'] = $this->nextPart($parseString, '^(LOCATE)[[:space:]]*');
-						// Strip off "("
-						$parseString = trim(substr($parseString, 1));
-						$stack[$level][$pnt[$level]]['func']['substr'] = $this->getValue($parseString);
-						if (!$this->nextPart($parseString, '^(,)')) {
-							return $this->parseError('No comma found as expected in parseWhereClause()', $parseString);
-						}
-						if ($fieldName = $this->nextPart($parseString, '^([[:alnum:]\\*._]+)[[:space:]]*')) {
-							// Parse field name into field and table:
-							$tableField = explode('.', $fieldName, 2);
-							if (count($tableField) === 2) {
-								$stack[$level][$pnt[$level]]['func']['table'] = $tableField[0];
-								$stack[$level][$pnt[$level]]['func']['field'] = $tableField[1];
-							} else {
-								$stack[$level][$pnt[$level]]['func']['table'] = '';
-								$stack[$level][$pnt[$level]]['func']['field'] = $tableField[0];
-							}
-						} else {
-							return $this->parseError('No field name found as expected in parseWhereClause()', $parseString);
-						}
-						if ($this->nextPart($parseString, '^(,)')) {
-							$stack[$level][$pnt[$level]]['func']['pos'] = $this->getValue($parseString);
-						}
-						if (!$this->nextPart($parseString, '^([)])')) {
-							return $this->parseError('No ) parenthesis at end of function', $parseString);
-						}
-					} elseif (preg_match('/^IFNULL[[:space:]]*[(]/i', $parseString)) {
-						$stack[$level][$pnt[$level]]['func']['type'] = $this->nextPart($parseString, '^(IFNULL)[[:space:]]*');
-						$parseString = trim(substr($parseString, 1));
-						// Strip off "("
-						if ($fieldName = $this->nextPart($parseString, '^([[:alnum:]\\*._]+)[[:space:]]*')) {
-							// Parse field name into field and table:
-							$tableField = explode('.', $fieldName, 2);
-							if (count($tableField) === 2) {
-								$stack[$level][$pnt[$level]]['func']['table'] = $tableField[0];
-								$stack[$level][$pnt[$level]]['func']['field'] = $tableField[1];
-							} else {
-								$stack[$level][$pnt[$level]]['func']['table'] = '';
-								$stack[$level][$pnt[$level]]['func']['field'] = $tableField[0];
-							}
-						} else {
-							return $this->parseError('No field name found as expected in parseWhereClause()', $parseString);
-						}
-						if ($this->nextPart($parseString, '^(,)')) {
-							$stack[$level][$pnt[$level]]['func']['default'] = $this->getValue($parseString);
-						}
-						if (!$this->nextPart($parseString, '^([)])')) {
-							return $this->parseError('No ) parenthesis at end of function', $parseString);
-						}
-					} elseif (preg_match('/^CAST[[:space:]]*[(]/i', $parseString)) {
-						$stack[$level][$pnt[$level]]['func']['type'] = $this->nextPart($parseString, '^(CAST)[[:space:]]*');
-						$parseString = trim(substr($parseString, 1));
-						// Strip off "("
-						if ($fieldName = $this->nextPart($parseString, '^([[:alnum:]\\*._]+)[[:space:]]*')) {
-							// Parse field name into field and table:
-							$tableField = explode('.', $fieldName, 2);
-							if (count($tableField) === 2) {
-								$stack[$level][$pnt[$level]]['func']['table'] = $tableField[0];
-								$stack[$level][$pnt[$level]]['func']['field'] = $tableField[1];
-							} else {
-								$stack[$level][$pnt[$level]]['func']['table'] = '';
-								$stack[$level][$pnt[$level]]['func']['field'] = $tableField[0];
-							}
-						} else {
-							return $this->parseError('No field name found as expected in parseWhereClause()', $parseString);
-						}
-						if ($this->nextPart($parseString, '^([[:space:]]*AS[[:space:]]*)')) {
-							$stack[$level][$pnt[$level]]['func']['datatype'] = $this->getValue($parseString);
-						}
-						if (!$this->nextPart($parseString, '^([)])')) {
-							return $this->parseError('No ) parenthesis at end of function', $parseString);
-						}
-					} elseif (preg_match('/^FIND_IN_SET[[:space:]]*[(]/i', $parseString)) {
-						$stack[$level][$pnt[$level]]['func']['type'] = $this->nextPart($parseString, '^(FIND_IN_SET)[[:space:]]*');
-						// Strip off "("
-						$parseString = trim(substr($parseString, 1));
-						if ($str = $this->getValue($parseString)) {
-							$stack[$level][$pnt[$level]]['func']['str'] = $str;
-							if ($fieldName = $this->nextPart($parseString, '^,[[:space:]]*([[:alnum:]._]+)[[:space:]]*', TRUE)) {
-								// Parse field name into field and table:
-								$tableField = explode('.', $fieldName, 2);
-								if (count($tableField) === 2) {
-									$stack[$level][$pnt[$level]]['func']['table'] = $tableField[0];
-									$stack[$level][$pnt[$level]]['func']['field'] = $tableField[1];
-								} else {
-									$stack[$level][$pnt[$level]]['func']['table'] = '';
-									$stack[$level][$pnt[$level]]['func']['field'] = $tableField[0];
-								}
-							} else {
-								return $this->parseError('No field name found as expected in parseWhereClause()', $parseString);
-							}
-							if (!$this->nextPart($parseString, '^([)])')) {
-								return $this->parseError('No ) parenthesis at end of function', $parseString);
-							}
-						} else {
-							return $this->parseError('No item to look for found as expected in parseWhereClause()', $parseString);
-						}
-					} else {
-						// Support calculated value only for:
-						// - "&" (boolean AND)
-						// - "+" (addition)
-						// - "-" (substraction)
-						// - "*" (multiplication)
-						// - "/" (division)
-						// - "%" (modulo)
-						$calcOperators = '&|\\+|-|\\*|\\/|%';
-						// Fieldname:
-						if (($fieldName = $this->nextPart($parseString, '^([[:alnum:]._]+)([[:space:]]+|' . $calcOperators . '|<=|>=|<|>|=|!=|IS)')) !== '') {
-							// Parse field name into field and table:
-							$tableField = explode('.', $fieldName, 2);
-							if (count($tableField) === 2) {
-								$stack[$level][$pnt[$level]]['table'] = $tableField[0];
-								$stack[$level][$pnt[$level]]['field'] = $tableField[1];
-							} else {
-								$stack[$level][$pnt[$level]]['table'] = '';
-								$stack[$level][$pnt[$level]]['field'] = $tableField[0];
-							}
-						} else {
-							return $this->parseError('No field name found as expected in parseWhereClause()', $parseString);
-						}
-						// See if the value is calculated:
-						$stack[$level][$pnt[$level]]['calc'] = $this->nextPart($parseString, '^(' . $calcOperators . ')');
-						if ((string)$stack[$level][$pnt[$level]]['calc'] !== '') {
-							// Finding value for calculation:
-							$calc_value = $this->getValue($parseString);
-							$stack[$level][$pnt[$level]]['calc_value'] = $calc_value;
-							if (count($calc_value) === 1 && is_string($calc_value[0])) {
-								// Value is a field, store it to allow DBAL to post-process it (quoting, remapping)
-								$tableField = explode('.', $calc_value[0], 2);
-								if (count($tableField) === 2) {
-									$stack[$level][$pnt[$level]]['calc_table'] = $tableField[0];
-									$stack[$level][$pnt[$level]]['calc_field'] = $tableField[1];
-								} else {
-									$stack[$level][$pnt[$level]]['calc_table'] = '';
-									$stack[$level][$pnt[$level]]['calc_field'] = $tableField[0];
-								}
-							}
-						}
-					}
-					$stack[$level][$pnt[$level]]['comparator'] = $this->nextPart($parseString, '^(' . implode('|', self::$comparatorPatterns) . ')');
-					if ($stack[$level][$pnt[$level]]['comparator'] !== '') {
-						if (preg_match('/^CONCAT[[:space:]]*\\(/', $parseString)) {
-							$this->nextPart($parseString, '^(CONCAT[[:space:]]?[(])');
-							$values = array(
-								'operator' => 'CONCAT',
-								'args' => array()
-							);
-							$cnt = 0;
-							while ($fieldName = $this->nextPart($parseString, '^([[:alnum:]._]+)')) {
-								// Parse field name into field and table:
-								$tableField = explode('.', $fieldName, 2);
-								if (count($tableField) === 2) {
-									$values['args'][$cnt]['table'] = $tableField[0];
-									$values['args'][$cnt]['field'] = $tableField[1];
-								} else {
-									$values['args'][$cnt]['table'] = '';
-									$values['args'][$cnt]['field'] = $tableField[0];
-								}
-								// Looking for comma:
-								$this->nextPart($parseString, '^(,)');
-								$cnt++;
-							}
-							// Look for ending parenthesis:
-							$this->nextPart($parseString, '([)])');
-							$stack[$level][$pnt[$level]]['value'] = $values;
-						} else {
-							if (\TYPO3\CMS\Core\Utility\GeneralUtility::inList('IN,NOT IN', $stack[$level][$pnt[$level]]['comparator']) && preg_match('/^[(][[:space:]]*SELECT[[:space:]]+/', $parseString)) {
-								$this->nextPart($parseString, '^([(])');
-								$stack[$level][$pnt[$level]]['subquery'] = $this->parseSELECT($parseString, $parameterReferences);
-								// Seek to new position in parseString after parsing of the subquery
-								if (!empty($stack[$level][$pnt[$level]]['subquery']['parseString'])) {
-									$parseString = $stack[$level][$pnt[$level]]['subquery']['parseString'];
-									unset($stack[$level][$pnt[$level]]['subquery']['parseString']);
-								}
-								if (!$this->nextPart($parseString, '^([)])')) {
-									return 'No ) parenthesis at end of subquery';
-								}
-							} else {
-								if (\TYPO3\CMS\Core\Utility\GeneralUtility::inList('BETWEEN,NOT BETWEEN', $stack[$level][$pnt[$level]]['comparator'])) {
-									$stack[$level][$pnt[$level]]['values'] = array();
-									$stack[$level][$pnt[$level]]['values'][0] = $this->getValue($parseString);
-									if (!$this->nextPart($parseString, '^(AND)')) {
-										return $this->parseError('No AND operator found as expected in parseWhereClause()', $parseString);
-									}
-									$stack[$level][$pnt[$level]]['values'][1] = $this->getValue($parseString);
-								} else {
-									// Finding value for comparator:
-									$stack[$level][$pnt[$level]]['value'] = &$this->getValueOrParameter($parseString, $stack[$level][$pnt[$level]]['comparator'], '', $parameterReferences);
-									if ($this->parse_error) {
-										return $this->parse_error;
-									}
-								}
-							}
-						}
-					}
-				}
-				// Finished, increase pointer:
-				$pnt[$level]++;
-				// Checking if we are back to level 0 and we should still decrease level,
-				// meaning we were probably parsing as subquery and should return here:
-				if ($level === 0 && preg_match('/^[)]/', $parseString)) {
-					// Return the stacks lowest level:
-					return $stack[0];
-				}
-				// Checking if we are back to level 0 and we should still decrease level,
-				// meaning we were probably parsing a subquery and should return here:
-				if ($level === 0 && preg_match('/^[)]/', $parseString)) {
-					// Return the stacks lowest level:
-					return $stack[0];
-				}
-				// Checking if the current level is ended, in that case do stack management:
-				while ($this->nextPart($parseString, '^([)])')) {
-					$level--;
-					// Decrease level:
-					// Copy stack
-					$stack[$level][$pnt[$level]]['sub'] = $stack[$level + 1];
-					// Increase pointer of the new level
-					$pnt[$level]++;
-					// Make recursivity check:
-					$loopExit++;
-					if ($loopExit > 500) {
-						return $this->parseError('More than 500 loops (in search for exit parenthesis), exiting prematurely in parseWhereClause()...', $parseString);
-					}
-				}
-				// Detecting the operator for the next level:
-				$op = $this->nextPart($parseString, '^(AND[[:space:]]+NOT|&&[[:space:]]+NOT|OR[[:space:]]+NOT|OR[[:space:]]+NOT|\\|\\|[[:space:]]+NOT|AND|&&|OR|\\|\\|)(\\(|[[:space:]]+)');
-				if ($op) {
-					// Normalize boolean operator
-					$op = str_replace(array('&&', '||'), array('AND', 'OR'), $op);
-					$stack[$level][$pnt[$level]]['operator'] = $op;
-				} elseif ($parseString !== '') {
-					// Looking for stop-keywords:
-					if ($stopRegex && ($this->lastStopKeyWord = $this->nextPart($parseString, $stopRegex))) {
-						$this->lastStopKeyWord = $this->normalizeKeyword($this->lastStopKeyWord);
-						return $stack[0];
-					} else {
-						return $this->parseError('No operator, but parsing not finished in parseWhereClause().', $parseString);
-					}
-				}
-			}
-			// Make recursivity check:
-			$loopExit++;
-			if ($loopExit > 500) {
-				return $this->parseError('More than 500 loops, exiting prematurely in parseWhereClause()...', $parseString);
-			}
-		}
-		// Return the stacks lowest level:
-		return $stack[0];
-	}
-
-	/**
-	 * Parsing the WHERE clause fields in the "WHERE [$parseString] ..." part of a query into a multidimensional array.
-	 * The success of this parsing determines if that part of the query is supported by TYPO3.
-	 *
-	 * @param string $parseString WHERE clause to parse. NOTICE: passed by reference!
-	 * @param string $stopRegex Regular expressing to STOP parsing, eg. '^(GROUP BY|ORDER BY|LIMIT)([[:space:]]*)'
-	 * @return mixed If successful parsing, returns an array, otherwise an error string.
-	 */
-	public function parseFieldDef(&$parseString, $stopRegex = '') {
-		// Prepare variables:
-		$parseString = $this->trimSQL($parseString);
-		$this->lastStopKeyWord = '';
-		$this->parse_error = '';
-		$result = array();
-		// Field type:
-		if ($result['fieldType'] = $this->nextPart($parseString, '^(int|smallint|tinyint|mediumint|bigint|double|numeric|decimal|float|varchar|char|text|tinytext|mediumtext|longtext|blob|tinyblob|mediumblob|longblob)([[:space:],]+|\\()')) {
-			// Looking for value:
-			if ($parseString[0] === '(') {
-				$parseString = substr($parseString, 1);
-				if ($result['value'] = $this->nextPart($parseString, '^([^)]*)')) {
-					$parseString = ltrim(substr($parseString, 1));
-				} else {
-					return $this->parseError('No end-parenthesis for value found in parseFieldDef()!', $parseString);
-				}
-			}
-			// Looking for keywords
-			while ($keyword = $this->nextPart($parseString, '^(DEFAULT|NOT[[:space:]]+NULL|AUTO_INCREMENT|UNSIGNED)([[:space:]]+|,|\\))')) {
-				$keywordCmp = $this->normalizeKeyword($keyword);
-				$result['featureIndex'][$keywordCmp]['keyword'] = $keyword;
-				switch ($keywordCmp) {
-					case 'DEFAULT':
-						$result['featureIndex'][$keywordCmp]['value'] = $this->getValue($parseString);
-						break;
-				}
-			}
-		} else {
-			return $this->parseError('Field type unknown in parseFieldDef()!', $parseString);
-		}
-		return $result;
-	}
-
-	/************************************
-	 *
-	 * Parsing: Helper functions
-	 *
-	 ************************************/
-	/**
-	 * Strips off a part of the parseString and returns the matching part.
-	 * Helper function for the parsing methods.
-	 *
-	 * @param string $parseString Parse string; if $regex finds anything the value of the first () level will be stripped of the string in the beginning. Further $parseString is left-trimmed (on success). Notice; parsestring is passed by reference.
-	 * @param string $regex Regex to find a matching part in the beginning of the string. Rules: You MUST start the regex with "^" (finding stuff in the beginning of string) and the result of the first parenthesis is what will be returned to you (and stripped of the string). Eg. '^(AND|OR|&&)[[:space:]]+' will return AND, OR or && if found and having one of more whitespaces after it, plus shorten $parseString with that match and any space after (by ltrim())
-	 * @param bool $trimAll If set the full match of the regex is stripped of the beginning of the string!
-	 * @return string The value of the first parenthesis level of the REGEX.
-	 */
-	protected function nextPart(&$parseString, $regex, $trimAll = FALSE) {
-		$reg = array();
-		// Adding space char because [[:space:]]+ is often a requirement in regex's
-		if (preg_match('/' . $regex . '/i', $parseString . ' ', $reg)) {
-			$parseString = ltrim(substr($parseString, strlen($reg[$trimAll ? 0 : 1])));
-			return $reg[1];
-		}
-		// No match found
-		return '';
-	}
-
-	/**
-	 * Finds value or either named (:name) or question mark (?) parameter markers at the beginning
-	 * of $parseString, returns result and strips it of parseString.
-	 * This method returns a pointer to the parameter or value that was found. In case of a parameter
-	 * the pointer is a reference to the corresponding item in array $parameterReferences.
-	 *
-	 * @param string $parseString The parseString
-	 * @param string $comparator The comparator used before.
-	 * @param string $mode The mode, e.g., "INDEX
-	 * @param mixed The value (string/integer) or parameter (:name/?). Otherwise an array with error message in first key (0)
-	 */
-	protected function &getValueOrParameter(&$parseString, $comparator = '', $mode = '', array &$parameterReferences = array()) {
-		$parameter = $this->nextPart($parseString, '^(\\:[[:alnum:]_]+|\\?)');
-		if ($parameter === '?') {
-			if (!isset($parameterReferences['?'])) {
-				$parameterReferences['?'] = array();
-			}
-			$value = array('?');
-			$parameterReferences['?'][] = &$value;
-		} elseif ($parameter !== '') {
-			// named parameter
-			if (isset($parameterReferences[$parameter])) {
-				// Use the same reference as last time we encountered this parameter
-				$value = &$parameterReferences[$parameter];
-			} else {
-				$value = array($parameter);
-				$parameterReferences[$parameter] = &$value;
-			}
-		} else {
-			$value = $this->getValue($parseString, $comparator, $mode);
-		}
-		return $value;
-	}
-
-	/**
-	 * Finds value in beginning of $parseString, returns result and strips it of parseString
-	 *
-	 * @param string $parseString The parseString, eg. "(0,1,2,3) ..." or "('asdf','qwer') ..." or "1234 ..." or "'My string value here' ...
-	 * @param string $comparator The comparator used before. If "NOT IN" or "IN" then the value is expected to be a list of values. Otherwise just an integer (un-quoted) or string (quoted)
-	 * @param string $mode The mode, eg. "INDEX
-	 * @return mixed The value (string/integer). Otherwise an array with error message in first key (0)
-	 */
-	protected function getValue(&$parseString, $comparator = '', $mode = '') {
-		$value = '';
-		if (\TYPO3\CMS\Core\Utility\GeneralUtility::inList('NOTIN,IN,_LIST', strtoupper(str_replace(array(' ', LF, CR, TAB), '', $comparator)))) {
-			// List of values:
-			if ($this->nextPart($parseString, '^([(])')) {
-				$listValues = array();
-				$comma = ',';
-				while ($comma === ',') {
-					$listValues[] = $this->getValue($parseString);
-					if ($mode === 'INDEX') {
-						// Remove any length restriction on INDEX definition
-						$this->nextPart($parseString, '^([(]\\d+[)])');
-					}
-					$comma = $this->nextPart($parseString, '^([,])');
-				}
-				$out = $this->nextPart($parseString, '^([)])');
-				if ($out) {
-					if ($comparator === '_LIST') {
-						$kVals = array();
-						foreach ($listValues as $vArr) {
-							$kVals[] = $vArr[0];
-						}
-						return $kVals;
-					} else {
-						return $listValues;
-					}
-				} else {
-					return array($this->parseError('No ) parenthesis in list', $parseString));
-				}
-			} else {
-				return array($this->parseError('No ( parenthesis starting the list', $parseString));
-			}
-		} else {
-			// Just plain string value, in quotes or not:
-			// Quote?
-			$firstChar = $parseString[0];
-			switch ($firstChar) {
-				case '"':
-					$value = array($this->getValueInQuotes($parseString, '"'), '"');
-					break;
-				case '\'':
-					$value = array($this->getValueInQuotes($parseString, '\''), '\'');
-					break;
-				default:
-					$reg = array();
-					if (preg_match('/^([[:alnum:]._-]+(?:\\([0-9]+\\))?)/i', $parseString, $reg)) {
-						$parseString = ltrim(substr($parseString, strlen($reg[0])));
-						$value = array($reg[1]);
-					}
-			}
-		}
-		return $value;
-	}
-
-	/**
-	 * Get value in quotes from $parseString.
-	 * NOTICE: If a query being parsed was prepared for another database than MySQL this function should probably be changed
-	 *
-	 * @param string $parseString String from which to find value in quotes. Notice that $parseString is passed by reference and is shortend by the output of this function.
-	 * @param string $quote The quote used; input either " or '
-	 * @return string The value, passed through stripslashes() !
-	 */
-	protected function getValueInQuotes(&$parseString, $quote) {
-		$parts = explode($quote, substr($parseString, 1));
-		$buffer = '';
-		foreach ($parts as $k => $v) {
-			$buffer .= $v;
-			$reg = array();
-			preg_match('/\\\\$/', $v, $reg);
-			if ($reg && strlen($reg[0]) % 2) {
-				$buffer .= $quote;
-			} else {
-				$parseString = ltrim(substr($parseString, strlen($buffer) + 2));
-				return $this->parseStripslashes($buffer);
-			}
-		}
-	}
-
-	/**
-	 * Strip slashes function used for parsing
-	 * NOTICE: If a query being parsed was prepared for another database than MySQL this function should probably be changed
-	 *
-	 * @param string $str Input string
-	 * @return string Output string
-	 */
-	protected function parseStripslashes($str) {
-		$search = array('\\\\', '\\\'', '\\"', '\0', '\n', '\r', '\Z');
-		$replace = array('\\', '\'', '"', "\x00", "\x0a", "\x0d", "\x1a");
-
-		return str_replace($search, $replace, $str);
-	}
-
-	/**
-	 * Add slashes function used for compiling queries
-	 * NOTICE: If a query being parsed was prepared for another database than MySQL this function should probably be changed
-	 *
-	 * @param string $str Input string
-	 * @return string Output string
-	 */
-	protected function compileAddslashes($str) {
-		$search = array('\\', '\'', '"', "\x00", "\x0a", "\x0d", "\x1a");
-		$replace = array('\\\\', '\\\'', '\\"', '\0', '\n', '\r', '\Z');
-
-		return str_replace($search, $replace, $str);
-	}
-
-	/**
-	 * Setting the internal error message value, $this->parse_error and returns that value.
-	 *
-	 * @param string $msg Input error message
-	 * @param string $restQuery Remaining query to parse.
-	 * @return string Error message.
-	 */
-	protected function parseError($msg, $restQuery) {
-		$this->parse_error = 'SQL engine parse ERROR: ' . $msg . ': near "' . substr($restQuery, 0, 50) . '"';
-		return $this->parse_error;
-	}
-
-	/**
-	 * Trimming SQL as preparation for parsing.
-	 * ";" in the end is stripped off.
-	 * White space is trimmed away around the value
-	 * A single space-char is added in the end
-	 *
-	 * @param string $str Input string
-	 * @return string Output string
-	 */
-	protected function trimSQL($str) {
-		return rtrim(rtrim(trim($str), ';')) . ' ';
-	}
-
-	/*************************
-	 *
-	 * Compiling queries
-	 *
-	 *************************/
-	/**
-	 * Compiles an SQL query from components
-	 *
-	 * @param array $components Array of SQL query components
-	 * @return string SQL query
-	 * @see parseSQL()
-	 */
-	public function compileSQL($components) {
-		switch ($components['type']) {
-			case 'SELECT':
-				$query = $this->compileSELECT($components);
-				break;
-			case 'UPDATE':
-				$query = $this->compileUPDATE($components);
-				break;
-			case 'INSERT':
-				$query = $this->compileINSERT($components);
-				break;
-			case 'DELETE':
-				$query = $this->compileDELETE($components);
-				break;
-			case 'EXPLAIN':
-				$query = 'EXPLAIN ' . $this->compileSELECT($components);
-				break;
-			case 'DROPTABLE':
-				$query = 'DROP TABLE' . ($components['ifExists'] ? ' IF EXISTS' : '') . ' ' . $components['TABLE'];
-				break;
-			case 'CREATETABLE':
-				$query = $this->compileCREATETABLE($components);
-				break;
-			case 'ALTERTABLE':
-				$query = $this->compileALTERTABLE($components);
-				break;
-			case 'TRUNCATETABLE':
-				$query = $this->compileTRUNCATETABLE($components);
-				break;
-		}
-		return $query;
-	}
-
-	/**
-	 * Compiles a SELECT statement from components array
-	 *
-	 * @param array $components Array of SQL query components
-	 * @return string SQL SELECT query
-	 * @see parseSELECT()
-	 */
-	protected function compileSELECT($components) {
-		// Initialize:
-		$where = $this->compileWhereClause($components['WHERE']);
-		$groupBy = $this->compileFieldList($components['GROUPBY']);
-		$orderBy = $this->compileFieldList($components['ORDERBY']);
-		$limit = $components['LIMIT'];
-		// Make query:
-		$query = 'SELECT ' . ($components['STRAIGHT_JOIN'] ?: '') . ' ' .
-				$this->compileFieldList($components['SELECT']) .
-				' FROM ' . $this->compileFromTables($components['FROM']) . ($where !== '' ?
-				' WHERE ' . $where : '') . ($groupBy !== '' ?
-				' GROUP BY ' . $groupBy : '') . ($orderBy !== '' ?
-				' ORDER BY ' . $orderBy : '') . ((string)$limit !== '' ?
-				' LIMIT ' . $limit : '');
-		return $query;
-	}
-
-	/**
-	 * Compiles an UPDATE statement from components array
-	 *
-	 * @param array $components Array of SQL query components
-	 * @return string SQL UPDATE query
-	 * @see parseUPDATE()
-	 */
-	protected function compileUPDATE($components) {
-		// Where clause:
-		$where = $this->compileWhereClause($components['WHERE']);
-		// Fields
-		$fields = array();
-		foreach ($components['FIELDS'] as $fN => $fV) {
-			$fields[] = $fN . '=' . $fV[1] . $this->compileAddslashes($fV[0]) . $fV[1];
-		}
-		// Make query:
-		$query = 'UPDATE ' . $components['TABLE'] . ' SET ' . implode(',', $fields) .
-			($where !== '' ? ' WHERE ' . $where : '');
-
-		return $query;
-	}
-
-	/**
-	 * Compiles an INSERT statement from components array
-	 *
-	 * @param array $components Array of SQL query components
-	 * @return string SQL INSERT query
-	 * @see parseINSERT()
-	 */
-	protected function compileINSERT($components) {
-		$values = array();
-		if (isset($components['VALUES_ONLY']) && is_array($components['VALUES_ONLY'])) {
-			$valuesComponents = $components['EXTENDED'] === '1' ? $components['VALUES_ONLY'] : array($components['VALUES_ONLY']);
-			$tableFields = array();
-		} else {
-			$valuesComponents = $components['EXTENDED'] === '1' ? $components['FIELDS'] : array($components['FIELDS']);
-			$tableFields = array_keys($valuesComponents[0]);
-		}
-		foreach ($valuesComponents as $valuesComponent) {
-			$fields = array();
-			foreach ($valuesComponent as $fV) {
-				$fields[] = $fV[1] . $this->compileAddslashes($fV[0]) . $fV[1];
-			}
-			$values[] = '(' . implode(',', $fields) . ')';
-		}
-		// Make query:
-		$query = 'INSERT INTO ' . $components['TABLE'];
-		if (!empty($tableFields)) {
-			$query .= ' (' . implode(',', $tableFields) . ')';
-		}
-		$query .= ' VALUES ' . implode(',', $values);
-
-		return $query;
-	}
-
-	/**
-	 * Compiles an DELETE statement from components array
-	 *
-	 * @param array $components Array of SQL query components
-	 * @return string SQL DELETE query
-	 * @see parseDELETE()
-	 */
-	protected function compileDELETE($components) {
-		// Where clause:
-		$where = $this->compileWhereClause($components['WHERE']);
-		// Make query:
-		$query = 'DELETE FROM ' . $components['TABLE'] . ($where !== '' ? ' WHERE ' . $where : '');
-
-		return $query;
-	}
-
-	/**
-	 * Compiles a CREATE TABLE statement from components array
-	 *
-	 * @param array $components Array of SQL query components
-	 * @return string SQL CREATE TABLE query
-	 * @see parseCREATETABLE()
-	 */
-	protected function compileCREATETABLE($components) {
-		// Create fields and keys:
-		$fieldsKeys = array();
-		foreach ($components['FIELDS'] as $fN => $fCfg) {
-			$fieldsKeys[] = $fN . ' ' . $this->compileFieldCfg($fCfg['definition']);
-		}
-		if ($components['KEYS']) {
-			foreach ($components['KEYS'] as $kN => $kCfg) {
-				if ($kN === 'PRIMARYKEY') {
-					$fieldsKeys[] = 'PRIMARY KEY (' . implode(',', $kCfg) . ')';
-				} elseif ($kN === 'UNIQUE') {
-					$key = key($kCfg);
-					$fields = current($kCfg);
-					$fieldsKeys[] = 'UNIQUE KEY ' . $key . ' (' . implode(',', $fields) . ')';
-				} else {
-					$fieldsKeys[] = 'KEY ' . $kN . ' (' . implode(',', $kCfg) . ')';
-				}
-			}
-		}
-		// Make query:
-		$query = 'CREATE TABLE ' . $components['TABLE'] . ' (' .
-			implode(',', $fieldsKeys) . ')' .
-			($components['engine'] ? ' ENGINE=' . $components['engine'] : '');
-
-		return $query;
-	}
-
-	/**
-	 * Compiles an ALTER TABLE statement from components array
-	 *
-	 * @param array $components Array of SQL query components
-	 * @return string SQL ALTER TABLE query
-	 * @see parseALTERTABLE()
-	 */
-	protected function compileALTERTABLE($components) {
-		// Make query:
-		$query = 'ALTER TABLE ' . $components['TABLE'] . ' ' . $components['action'] . ' ' . ($components['FIELD'] ?: $components['KEY']);
-		// Based on action, add the final part:
-		switch ($this->normalizeKeyword($components['action'])) {
-			case 'ADD':
-				$query .= ' ' . $this->compileFieldCfg($components['definition']);
-				break;
-			case 'CHANGE':
-				$query .= ' ' . $components['newField'] . ' ' . $this->compileFieldCfg($components['definition']);
-				break;
-			case 'DROP':
-			case 'DROPKEY':
-				break;
-			case 'ADDKEY':
-			case 'ADDPRIMARYKEY':
-			case 'ADDUNIQUE':
-				$query .= ' (' . implode(',', $components['fields']) . ')';
-				break;
-			case 'DEFAULTCHARACTERSET':
-				$query .= $components['charset'];
-				break;
-			case 'ENGINE':
-				$query .= '= ' . $components['engine'];
-				break;
-		}
-		// Return query
-		return $query;
-	}
-
-	/**
-	 * Compiles a TRUNCATE TABLE statement from components array
-	 *
-	 * @param array $components Array of SQL query components
-	 * @return string SQL TRUNCATE TABLE query
-	 * @see parseTRUNCATETABLE()
-	 */
-	protected function compileTRUNCATETABLE(array $components) {
-		// Make query:
-		$query = 'TRUNCATE TABLE ' . $components['TABLE'];
-		// Return query
-		return $query;
-	}
-
-	/**************************************
-	 *
-	 * Compiling queries, helper functions for parts of queries
-	 *
-	 **************************************/
-	/**
-	 * Compiles a "SELECT [output] FROM..:" field list based on input array (made with ->parseFieldList())
-	 * Can also compile field lists for ORDER BY and GROUP BY.
-	 *
-	 * @param array $selectFields Array of select fields, (made with ->parseFieldList())
-	 * @param bool $compileComments Whether comments should be compiled
-	 * @return string Select field string
-	 * @see parseFieldList()
-	 */
-	public function compileFieldList($selectFields, $compileComments = TRUE) {
-		// Prepare buffer variable:
-		$fields = '';
-		// Traverse the selectFields if any:
-		if (is_array($selectFields)) {
-			$outputParts = array();
-			foreach ($selectFields as $k => $v) {
-				// Detecting type:
-				switch ($v['type']) {
-					case 'function':
-						$outputParts[$k] = $v['function'] . '(' . $v['func_content'] . ')';
-						break;
-					case 'flow-control':
-						if ($v['flow-control']['type'] === 'CASE') {
-							$outputParts[$k] = $this->compileCaseStatement($v['flow-control']);
-						}
-						break;
-					case 'field':
-						$outputParts[$k] = ($v['distinct'] ? $v['distinct'] : '') . ($v['table'] ? $v['table'] . '.' : '') . $v['field'];
-						break;
-				}
-				// Alias:
-				if ($v['as']) {
-					$outputParts[$k] .= ' ' . $v['as_keyword'] . ' ' . $v['as'];
-				}
-				// Specifically for ORDER BY and GROUP BY field lists:
-				if ($v['sortDir']) {
-					$outputParts[$k] .= ' ' . $v['sortDir'];
-				}
-			}
-			if ($compileComments && $selectFields[0]['comments']) {
-				$fields = $selectFields[0]['comments'] . ' ';
-			}
-			$fields .= implode(', ', $outputParts);
-		}
-		return $fields;
-	}
-
-	/**
-	 * Compiles a CASE ... WHEN flow-control construct based on input array (made with ->parseCaseStatement())
-	 *
-	 * @param array $components Array of case components, (made with ->parseCaseStatement())
-	 * @return string Case when string
-	 * @see parseCaseStatement()
-	 */
-	protected function compileCaseStatement(array $components) {
-		$statement = 'CASE';
-		if (isset($components['case_field'])) {
-			$statement .= ' ' . $components['case_field'];
-		} elseif (isset($components['case_value'])) {
-			$statement .= ' ' . $components['case_value'][1] . $components['case_value'][0] . $components['case_value'][1];
-		}
-		foreach ($components['when'] as $when) {
-			$statement .= ' WHEN ';
-			$statement .= $this->compileWhereClause($when['when_value']);
-			$statement .= ' THEN ';
-			$statement .= $when['then_value'][1] . $when['then_value'][0] . $when['then_value'][1];
-		}
-		if (isset($components['else'])) {
-			$statement .= ' ELSE ';
-			$statement .= $components['else'][1] . $components['else'][0] . $components['else'][1];
-		}
-		$statement .= ' END';
-		return $statement;
-	}
-
-	/**
-	 * Compile a "JOIN table ON [output] = ..." identifier
-	 *
-	 * @param array $identifierParts Array of identifier parts
-	 * @return string
-	 * @see parseCastStatement()
-	 * @see parseFromTables()
-	 */
-	protected function compileJoinIdentifier($identifierParts) {
-		if ($identifierParts['type'] === 'cast') {
-			return sprintf('CAST(%s AS %s)',
-				$identifierParts['table'] ? $identifierParts['table'] . '.' . $identifierParts['field'] : $identifierParts['field'],
-				$identifierParts['datatype'][0]
-			);
-		} else {
-			return $identifierParts['table'] ? $identifierParts['table'] . '.' . $identifierParts['field'] : $identifierParts['field'];
-		}
-	}
-
-	/**
-	 * Compiles a "FROM [output] WHERE..:" table list based on input array (made with ->parseFromTables())
-	 *
-	 * @param array $tablesArray Array of table names, (made with ->parseFromTables())
-	 * @return string Table name string
-	 * @see parseFromTables()
-	 */
-	public function compileFromTables($tablesArray) {
-		// Prepare buffer variable:
-		$outputParts = array();
-		// Traverse the table names:
-		if (is_array($tablesArray)) {
-			foreach ($tablesArray as $k => $v) {
-				// Set table name:
-				$outputParts[$k] = $v['table'];
-				// Add alias AS if there:
-				if ($v['as']) {
-					$outputParts[$k] .= ' ' . $v['as_keyword'] . ' ' . $v['as'];
-				}
-				if (is_array($v['JOIN'])) {
-					foreach ($v['JOIN'] as $join) {
-						$outputParts[$k] .= ' ' . $join['type'] . ' ' . $join['withTable'];
-						// Add alias AS if there:
-						if (isset($join['as']) && $join['as']) {
-							$outputParts[$k] .= ' ' . $join['as_keyword'] . ' ' . $join['as'];
-						}
-						$outputParts[$k] .= ' ON ';
-						foreach ($join['ON'] as $condition) {
-							if ($condition['operator'] !== '') {
-								$outputParts[$k] .= ' ' . $condition['operator'] . ' ';
-							}
-							$outputParts[$k] .= $this->compileJoinIdentifier($condition['left']);
-							$outputParts[$k] .= $condition['comparator'];
-							if (!empty($condition['right']['value'])) {
-								$value = $condition['right']['value'];
-								$outputParts[$k] .= $value[1] . $this->compileAddslashes($value[0]) . $value[1];
-							} else {
-								$outputParts[$k] .= $this->compileJoinIdentifier($condition['right']);
-							}
-						}
-					}
-				}
-			}
-		}
-		// Return imploded buffer:
-		return implode(', ', $outputParts);
-	}
-
-	/**
-	 * Implodes an array of WHERE clause configuration into a WHERE clause.
-	 *
-	 * @param array $clauseArray WHERE clause configuration
-	 * @return string WHERE clause as string.
-	 * @see explodeWhereClause()
-	 */
-	public function compileWhereClause($clauseArray) {
-		// Prepare buffer variable:
-		$output = '';
-		// Traverse clause array:
-		if (is_array($clauseArray)) {
-			foreach ($clauseArray as $k => $v) {
-				// Set operator:
-				$output .= $v['operator'] ? ' ' . $v['operator'] : '';
-				// Look for sublevel:
-				if (is_array($v['sub'])) {
-					$output .= ' (' . trim($this->compileWhereClause($v['sub'])) . ')';
-				} elseif (isset($v['func']) && $v['func']['type'] === 'EXISTS') {
-					$output .= ' ' . trim($v['modifier']) . ' EXISTS (' . $this->compileSELECT($v['func']['subquery']) . ')';
-				} else {
-					if (isset($v['func']) && $v['func']['type'] === 'LOCATE') {
-						$output .= ' ' . trim($v['modifier']) . ' LOCATE(';
-						$output .= $v['func']['substr'][1] . $v['func']['substr'][0] . $v['func']['substr'][1];
-						$output .= ', ' . ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
-						$output .= isset($v['func']['pos']) ? ', ' . $v['func']['pos'][0] : '';
-						$output .= ')';
-					} elseif (isset($v['func']) && $v['func']['type'] === 'IFNULL') {
-						$output .= ' ' . trim($v['modifier']) . ' IFNULL(';
-						$output .= ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
-						$output .= ', ' . $v['func']['default'][1] . $this->compileAddslashes($v['func']['default'][0]) . $v['func']['default'][1];
-						$output .= ')';
-					} elseif (isset($v['func']) && $v['func']['type'] === 'CAST') {
-						$output .= ' ' . trim($v['modifier']) . ' CAST(';
-						$output .= ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
-						$output .= ' AS ' . $v['func']['datatype'][0];
-						$output .= ')';
-					} elseif (isset($v['func']) && $v['func']['type'] === 'FIND_IN_SET') {
-						$output .= ' ' . trim($v['modifier']) . ' FIND_IN_SET(';
-						$output .= $v['func']['str'][1] . $v['func']['str'][0] . $v['func']['str'][1];
-						$output .= ', ' . ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
-						$output .= ')';
-					} else {
-						// Set field/table with modifying prefix if any:
-						$output .= ' ' . trim(($v['modifier'] . ' ' . ($v['table'] ? $v['table'] . '.' : '') . $v['field']));
-						// Set calculation, if any:
-						if ($v['calc']) {
-							$output .= $v['calc'] . $v['calc_value'][1] . $this->compileAddslashes($v['calc_value'][0]) . $v['calc_value'][1];
-						}
-					}
-					// Set comparator:
-					if ($v['comparator']) {
-						$output .= ' ' . $v['comparator'];
-						// Detecting value type; list or plain:
-						if (\TYPO3\CMS\Core\Utility\GeneralUtility::inList('NOTIN,IN', $this->normalizeKeyword($v['comparator']))) {
-							if (isset($v['subquery'])) {
-								$output .= ' (' . $this->compileSELECT($v['subquery']) . ')';
-							} else {
-								$valueBuffer = array();
-								foreach ($v['value'] as $realValue) {
-									$valueBuffer[] = $realValue[1] . $this->compileAddslashes($realValue[0]) . $realValue[1];
-								}
-								$output .= ' (' . trim(implode(',', $valueBuffer)) . ')';
-							}
-						} else {
-							if (\TYPO3\CMS\Core\Utility\GeneralUtility::inList('BETWEEN,NOT BETWEEN', $v['comparator'])) {
-								$lbound = $v['values'][0];
-								$ubound = $v['values'][1];
-								$output .= ' ' . $lbound[1] . $this->compileAddslashes($lbound[0]) . $lbound[1];
-								$output .= ' AND ';
-								$output .= $ubound[1] . $this->compileAddslashes($ubound[0]) . $ubound[1];
-							} else {
-								if (isset($v['value']['operator'])) {
-									$values = array();
-									foreach ($v['value']['args'] as $fieldDef) {
-										$values[] = ($fieldDef['table'] ? $fieldDef['table'] . '.' : '') . $fieldDef['field'];
-									}
-									$output .= ' ' . $v['value']['operator'] . '(' . implode(',', $values) . ')';
-								} else {
-									$output .= ' ' . $v['value'][1] . $this->compileAddslashes($v['value'][0]) . $v['value'][1];
-								}
-							}
-						}
-					}
-				}
-			}
-		}
-		// Return output buffer:
-		return $output;
-	}
-
-	/**
-	 * Compile field definition
-	 *
-	 * @param array $fieldCfg Field definition parts
-	 * @return string Field definition string
-	 */
-	public function compileFieldCfg($fieldCfg) {
-		// Set type:
-		$cfg = $fieldCfg['fieldType'];
-		// Add value, if any:
-		if ((string)$fieldCfg['value'] !== '') {
-			$cfg .= '(' . $fieldCfg['value'] . ')';
-		}
-		// Add additional features:
-		if (is_array($fieldCfg['featureIndex'])) {
-			foreach ($fieldCfg['featureIndex'] as $featureDef) {
-				$cfg .= ' ' . $featureDef['keyword'];
-				// Add value if found:
-				if (is_array($featureDef['value'])) {
-					$cfg .= ' ' . $featureDef['value'][1] . $this->compileAddslashes($featureDef['value'][0]) . $featureDef['value'][1];
-				}
-			}
-		}
-		// Return field definition string:
-		return $cfg;
-	}
-
-	/*************************
-	 *
-	 * Debugging
-	 *
-	 *************************/
-	/**
-	 * Check parsability of input SQL part string; Will parse and re-compile after which it is compared
-	 *
-	 * @param string $part Part definition of string; "SELECT" = fieldlist (also ORDER BY and GROUP BY), "FROM" = table list, "WHERE" = Where clause.
-	 * @param string $str SQL string to verify parsability of
-	 * @return mixed Returns array with string 1 and 2 if error, otherwise FALSE
-	 */
-	public function debug_parseSQLpart($part, $str) {
-		$retVal = FALSE;
-		switch ($part) {
-			case 'SELECT':
-				$retVal = $this->debug_parseSQLpartCompare($str, $this->compileFieldList($this->parseFieldList($str)));
-				break;
-			case 'FROM':
-				$retVal = $this->debug_parseSQLpartCompare($str, $this->compileFromTables($this->parseFromTables($str)));
-				break;
-			case 'WHERE':
-				$retVal = $this->debug_parseSQLpartCompare($str, $this->compileWhereClause($this->parseWhereClause($str)));
-				break;
-		}
-		return $retVal;
-	}
-
-	/**
-	 * Compare two query strings by stripping away whitespace.
-	 *
-	 * @param string $str SQL String 1
-	 * @param string $newStr SQL string 2
-	 * @param bool $caseInsensitive If TRUE, the strings are compared insensitive to case
-	 * @return mixed Returns array with string 1 and 2 if error, otherwise FALSE
-	 */
-	public function debug_parseSQLpartCompare($str, $newStr, $caseInsensitive = FALSE) {
-		if ($caseInsensitive) {
-			$str1 = strtoupper($str);
-			$str2 = strtoupper($newStr);
-		} else {
-			$str1 = $str;
-			$str2 = $newStr;
-		}
-
-		// Fixing escaped chars:
-		$search = array(NUL, LF, CR, SUB);
-		$replace = array("\x00", "\x0a", "\x0d", "\x1a");
-		$str1 = str_replace($search, $replace, $str1);
-		$str2 = str_replace($search, $replace, $str2);
-
-		$search = self::$interQueryWhitespaces;
-		if (str_replace($search, '', $this->trimSQL($str1)) !== str_replace($search, '', $this->trimSQL($str2))) {
-			return array(
-				str_replace($search, ' ', $str),
-				str_replace($search, ' ', $newStr),
-			);
-		}
-	}
-
-	/**
-	 * Normalizes the keyword by removing any separator and changing to uppercase
-	 *
-	 * @param string $keyword The keyword being normalized
-	 * @return string
-	 */
-	protected function normalizeKeyword($keyword) {
-		return strtoupper(str_replace(self::$interQueryWhitespaces, '', $keyword));
-	}
-}
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Breaking-68401-SqlParserMovedIntoEXTdbal.rst b/typo3/sysext/core/Documentation/Changelog/master/Breaking-68401-SqlParserMovedIntoEXTdbal.rst
new file mode 100644
index 0000000000000000000000000000000000000000..bbdcf3d55cd68a23082fa9604c3f691e62bb76bf
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/master/Breaking-68401-SqlParserMovedIntoEXTdbal.rst
@@ -0,0 +1,36 @@
+================================================
+Breaking: #68401 - SqlParser moved into EXT:dbal
+================================================
+
+Description
+===========
+
+The SQL Parser included with the core has not been in use by anything
+except EXT:dbal for some time. The SQL parser has been merged with the
+version in EXT:dbal which now provides parsing and compiling of SQL
+statements for MySQL as well as other DBMS.
+
+
+Impact
+======
+
+There is no impact for the core as EXT:dbal was the sole user of the SQL
+parser and it has been migrated into EXT:dbal.
+
+As the parsing and the compiling of SQL statements has been separated into
+multiple classes the non-public interface of ``SqlParser`` has changed.
+Classes extending SqlParser need to be adjusted to the new interface.
+
+
+Affected Installations
+======================
+
+Installations with 3rd party extensions that use ``\TYPO3\CMS\Core\Database\SqlParser``.
+
+
+Migration
+=========
+
+Update the code to use ``\TYPO3\CMS\Dbal\Database\SqlParser`` instead of
+``\TYPO3\CMS\Core\Database\SqlParser`` or install EXT:compatibility6 which
+maps the old class names to the new ones in EXT:dbal.
diff --git a/typo3/sysext/core/Tests/Unit/Database/SqlParserTest.php b/typo3/sysext/core/Tests/Unit/Database/SqlParserTest.php
deleted file mode 100644
index 115287d91549c1c6b99501d53594a49a1f01d90b..0000000000000000000000000000000000000000
--- a/typo3/sysext/core/Tests/Unit/Database/SqlParserTest.php
+++ /dev/null
@@ -1,492 +0,0 @@
-<?php
-namespace TYPO3\CMS\Core\Tests\Unit\Database;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * Testcase for TYPO3\CMS\Core\Database\SqlParser
- */
-class SqlParserTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
-
-	/**
-	 * @var \TYPO3\CMS\Core\Database\SqlParser|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface
-	 */
-	protected $subject;
-
-	protected function setUp() {
-		$this->subject = $this->getAccessibleMock(\TYPO3\CMS\Core\Database\SqlParser::class, array('dummy'));
-	}
-
-	/**
-	 * Regression test
-	 *
-	 * @test
-	 */
-	public function compileWhereClauseDoesNotDropClauses() {
-		$clauses = array(
-			0 => array(
-				'modifier' => '',
-				'table' => 'pages',
-				'field' => 'fe_group',
-				'calc' => '',
-				'comparator' => '=',
-				'value' => array(
-					0 => '',
-					1 => '\''
-				)
-			),
-			1 => array(
-				'operator' => 'OR',
-				'modifier' => '',
-				'func' => array(
-					'type' => 'IFNULL',
-					'default' => array(
-						0 => '1',
-						1 => '\''
-					),
-					'table' => 'pages',
-					'field' => 'fe_group'
-				)
-			),
-			2 => array(
-				'operator' => 'OR',
-				'modifier' => '',
-				'table' => 'pages',
-				'field' => 'fe_group',
-				'calc' => '',
-				'comparator' => '=',
-				'value' => array(
-					0 => '0',
-					1 => '\''
-				)
-			),
-			3 => array(
-				'operator' => 'OR',
-				'modifier' => '',
-				'func' => array(
-					'type' => 'FIND_IN_SET',
-					'str' => array(
-						0 => '0',
-						1 => '\''
-					),
-					'table' => 'pages',
-					'field' => 'fe_group'
-				),
-				'comparator' => ''
-			),
-			4 => array(
-				'operator' => 'OR',
-				'modifier' => '',
-				'func' => array(
-					'type' => 'FIND_IN_SET',
-					'str' => array(
-						0 => '-1',
-						1 => '\''
-					),
-					'table' => 'pages',
-					'field' => 'fe_group'
-				),
-				'comparator' => ''
-			),
-			5 => array(
-				'operator' => 'OR',
-				'modifier' => '',
-				'func' => array(
-					'type' => 'CAST',
-					'table' => 'pages',
-					'field' => 'fe_group',
-					'datatype' => 'CHAR'
-				),
-				'comparator' => '=',
-				'value' => array(
-					0 => '',
-					1 => '\''
-				)
-			)
-		);
-		$output = $this->subject->compileWhereClause($clauses);
-		$parts = explode(' OR ', $output);
-		$this->assertSame(count($clauses), count($parts));
-		$this->assertContains('IFNULL', $output);
-	}
-
-	/**
-	 * Data provider for trimSqlReallyTrimsAllWhitespace
-	 *
-	 * @see trimSqlReallyTrimsAllWhitespace
-	 */
-	public function trimSqlReallyTrimsAllWhitespaceDataProvider() {
-		return array(
-			'Nothing to trim' => array('SELECT * FROM test WHERE 1=1;', 'SELECT * FROM test WHERE 1=1 '),
-			'Space after ;' => array('SELECT * FROM test WHERE 1=1; ', 'SELECT * FROM test WHERE 1=1 '),
-			'Space before ;' => array('SELECT * FROM test WHERE 1=1 ;', 'SELECT * FROM test WHERE 1=1 '),
-			'Space before and after ;' => array('SELECT * FROM test WHERE 1=1 ; ', 'SELECT * FROM test WHERE 1=1 '),
-			'Linefeed after ;' => array('SELECT * FROM test WHERE 1=1' . LF . ';', 'SELECT * FROM test WHERE 1=1 '),
-			'Linefeed before ;' => array('SELECT * FROM test WHERE 1=1;' . LF, 'SELECT * FROM test WHERE 1=1 '),
-			'Linefeed before and after ;' => array('SELECT * FROM test WHERE 1=1' . LF . ';' . LF, 'SELECT * FROM test WHERE 1=1 '),
-			'Tab after ;' => array('SELECT * FROM test WHERE 1=1' . TAB . ';', 'SELECT * FROM test WHERE 1=1 '),
-			'Tab before ;' => array('SELECT * FROM test WHERE 1=1;' . TAB, 'SELECT * FROM test WHERE 1=1 '),
-			'Tab before and after ;' => array('SELECT * FROM test WHERE 1=1' . TAB . ';' . TAB, 'SELECT * FROM test WHERE 1=1 '),
-		);
-	}
-
-	/**
-	 * @test
-	 * @dataProvider trimSqlReallyTrimsAllWhitespaceDataProvider
-	 * @param string $sql The SQL to trim
-	 * @param string $expected The expected trimmed SQL with single space at the end
-	 */
-	public function trimSqlReallyTrimsAllWhitespace($sql, $expected) {
-		$result = $this->subject->_call('trimSQL', $sql);
-		$this->assertSame($expected, $result);
-	}
-
-
-	/**
-	 * Data provider for getValueReturnsCorrectValues
-	 *
-	 * @see getValueReturnsCorrectValues
-	 */
-	public function getValueReturnsCorrectValuesDataProvider() {
-		return array(
-			// description => array($parseString, $comparator, $mode, $expected)
-			'key definition without length' => array('(pid,input_1), ', '_LIST', 'INDEX', array('pid', 'input_1')),
-			'key definition with length' => array('(pid,input_1(30)), ', '_LIST', 'INDEX', array('pid', 'input_1(30)')),
-			'key definition without length (no mode)' => array('(pid,input_1), ', '_LIST', '',  array('pid', 'input_1')),
-			'key definition with length (no mode)' => array('(pid,input_1(30)), ', '_LIST', '', array('pid', 'input_1(30)')),
-			'test1' => array('input_1 varchar(255) DEFAULT \'\' NOT NULL,', '', '', array('input_1')),
-			'test2' => array('varchar(255) DEFAULT \'\' NOT NULL,', '', '', array('varchar(255)')),
-			'test3' => array('DEFAULT \'\' NOT NULL,', '', '', array('DEFAULT')),
-			'test4' => array('\'\' NOT NULL,', '', '', array('', '\'')),
-			'test5' => array('NOT NULL,', '', '', array('NOT')),
-			'test6' => array('NULL,', '', '', array('NULL')),
-			'getValueOrParameter' => array('NULL,', '', '', array('NULL')),
-		);
-	}
-
-	/**
-	 * @test
-	 * @dataProvider getValueReturnsCorrectValuesDataProvider
-	 * @param string $parseString the string to parse
-	 * @param string $comparator The comparator used before. If "NOT IN" or "IN" then the value is expected to be a list of values. Otherwise just an integer (un-quoted) or string (quoted)
-	 * @param string $mode The mode, eg. "INDEX
-	 * @param string $expected
-	 */
-	public function getValueReturnsCorrectValues($parseString, $comparator, $mode, $expected) {
-		$result = $this->subject->_callRef('getValue', $parseString, $comparator, $mode);
-		$this->assertSame($expected, $result);
-	}
-
-	/**
-	 * Data provider for parseSQL
-	 *
-	 * @see parseSQL
-	 */
-	public function parseSQLDataProvider() {
-		$testSql = array();
-		$testSql[] = 'CREATE TABLE tx_demo (';
-		$testSql[] = '	uid int(11) NOT NULL auto_increment,';
-		$testSql[] = '	pid int(11) DEFAULT \'0\' NOT NULL,';
-
-		$testSql[] = '	tstamp int(11) unsigned DEFAULT \'0\' NOT NULL,';
-		$testSql[] = '	crdate int(11) unsigned DEFAULT \'0\' NOT NULL,';
-		$testSql[] = '	cruser_id int(11) unsigned DEFAULT \'0\' NOT NULL,';
-		$testSql[] = '	deleted tinyint(4) unsigned DEFAULT \'0\' NOT NULL,';
-		$testSql[] = '	hidden tinyint(4) unsigned DEFAULT \'0\' NOT NULL,';
-		$testSql[] = '	starttime int(11) unsigned DEFAULT \'0\' NOT NULL,';
-		$testSql[] = '	endtime int(11) unsigned DEFAULT \'0\' NOT NULL,';
-
-		$testSql[] = '	input_1 varchar(255) DEFAULT \'\' NOT NULL,';
-		$testSql[] = '	input_2 varchar(255) DEFAULT \'\' NOT NULL,';
-		$testSql[] = '	select_child int(11) unsigned DEFAULT \'0\' NOT NULL,';
-
-		$testSql[] = '	PRIMARY KEY (uid),';
-		$testSql[] = '	KEY parent (pid,input_1),';
-		$testSql[] = '	KEY bar (tstamp,input_1(200),input_2(100),endtime)';
-		$testSql[] = ');';
-		$testSql = implode("\n", $testSql);
-		$expected = array(
-			'type' => 'CREATETABLE',
-			'TABLE' => 'tx_demo',
-			'FIELDS' => array(
-				'uid' => array(
-					'definition' => array(
-						'fieldType' => 'int',
-						'value' => '11',
-						'featureIndex' => array(
-							'NOTNULL' => array(
-								'keyword' => 'NOT NULL'
-							),
-							'AUTO_INCREMENT' => array(
-								'keyword' => 'auto_increment'
-							)
-						)
-					)
-				),
-				'pid' => array(
-					'definition' => array(
-						'fieldType' => 'int',
-						'value' => '11',
-						'featureIndex' => array(
-							'DEFAULT' => array(
-								'keyword' => 'DEFAULT',
-								'value' => array(
-									0 => '0',
-									1 => '\'',
-								)
-							),
-							'NOTNULL' => array(
-								'keyword' => 'NOT NULL'
-							)
-						)
-					)
-				),
-				'tstamp' => array(
-					'definition' => array(
-						'fieldType' => 'int',
-						'value' => '11',
-						'featureIndex' => array(
-							'UNSIGNED' => array(
-								'keyword' => 'unsigned'
-							),
-							'DEFAULT' => array(
-								'keyword' => 'DEFAULT',
-								'value' => array(
-									0 => '0',
-									1 => '\''
-								)
-							),
-							'NOTNULL' => array(
-								'keyword' => 'NOT NULL'
-							)
-						)
-					)
-				),
-				'crdate' => array(
-					'definition' => array(
-						'fieldType' => 'int',
-						'value' => '11',
-						'featureIndex' => array(
-							'UNSIGNED' => array(
-								'keyword' => 'unsigned'
-							),
-							'DEFAULT' => array(
-								'keyword' => 'DEFAULT',
-								'value' => array(
-									0 => '0',
-									1 => '\''
-								)
-							),
-							'NOTNULL' => array(
-								'keyword' => 'NOT NULL'
-							)
-						)
-					)
-				),
-				'cruser_id' => array(
-					'definition' => array(
-						'fieldType' => 'int',
-						'value' => '11',
-						'featureIndex' => array(
-							'UNSIGNED' => array(
-								'keyword' => 'unsigned'
-							),
-							'DEFAULT' => array(
-								'keyword' => 'DEFAULT',
-								'value' => array(
-									0 => '0',
-									1 => '\'',
-								)
-							),
-							'NOTNULL' => array(
-								'keyword' => 'NOT NULL'
-							)
-						)
-					)
-				),
-				'deleted' => array(
-					'definition' => array(
-						'fieldType' => 'tinyint',
-						'value' => '4',
-						'featureIndex' => array(
-							'UNSIGNED' => array(
-								'keyword' => 'unsigned'
-							),
-							'DEFAULT' => array(
-								'keyword' => 'DEFAULT',
-								'value' => array(
-									0 => '0',
-									1 => '\''
-								)
-							),
-							'NOTNULL' => array(
-								'keyword' => 'NOT NULL'
-							)
-						)
-					)
-				),
-				'hidden' => array(
-					'definition' => array(
-						'fieldType' => 'tinyint',
-						'value' => '4',
-						'featureIndex' => array(
-							'UNSIGNED' => array(
-								'keyword' => 'unsigned'
-							),
-							'DEFAULT' => array(
-								'keyword' => 'DEFAULT',
-								'value' => array(
-									0 => '0',
-									1 => '\''
-								)
-							),
-							'NOTNULL' => array(
-								'keyword' => 'NOT NULL'
-							)
-						)
-					)
-				),
-				'starttime' => array(
-					'definition' => array(
-						'fieldType' => 'int',
-						'value' => '11',
-						'featureIndex' => array(
-							'UNSIGNED' => array(
-								'keyword' => 'unsigned'
-							),
-							'DEFAULT' => array(
-								'keyword' => 'DEFAULT',
-								'value' => array(
-									0 => '0',
-									1 => '\''
-								)
-							),
-							'NOTNULL' => array(
-								'keyword' => 'NOT NULL'
-							)
-						)
-					)
-				),
-				'endtime' => array(
-					'definition' => array(
-						'fieldType' => 'int',
-						'value' => '11',
-						'featureIndex' => array(
-							'UNSIGNED' => array(
-								'keyword' => 'unsigned'
-							),
-							'DEFAULT' => array(
-								'keyword' => 'DEFAULT',
-								'value' => array(
-									0 => '0',
-									1 => '\'',
-								)
-							),
-							'NOTNULL' => array(
-								'keyword' => 'NOT NULL'
-							)
-						)
-					)
-				),
-				'input_1' => array(
-					'definition' => array(
-						'fieldType' => 'varchar',
-						'value' => '255',
-						'featureIndex' => array(
-							'DEFAULT' => array(
-								'keyword' => 'DEFAULT',
-								'value' => array(
-									0 => '',
-									1 => '\'',
-								)
-							),
-							'NOTNULL' => array(
-								'keyword' => 'NOT NULL'
-							)
-						)
-					)
-				),
-				'input_2' => array(
-					'definition' => array(
-						'fieldType' => 'varchar',
-						'value' => '255',
-						'featureIndex' => array(
-							'DEFAULT' => array(
-								'keyword' => 'DEFAULT',
-								'value' => array(
-									0 => '',
-									1 => '\'',
-								)
-							),
-							'NOTNULL' => array(
-								'keyword' => 'NOT NULL'
-							)
-						)
-					)
-				),
-				'select_child' => array(
-					'definition' => array(
-						'fieldType' => 'int',
-						'value' => '11',
-						'featureIndex' => array(
-							'UNSIGNED' => array(
-								'keyword' => 'unsigned'
-							),
-							'DEFAULT' => array(
-								'keyword' => 'DEFAULT',
-								'value' => array(
-									0 => '0',
-									1 => '\''
-								)
-							),
-							'NOTNULL' => array(
-								'keyword' => 'NOT NULL'
-							)
-						)
-					)
-				)
-			),
-			'KEYS' => array(
-				'PRIMARYKEY' => array(
-					0 => 'uid'
-				),
-				'parent' => array(
-					0 => 'pid',
-					1 => 'input_1',
-				),
-				'bar' => array(
-					0 => 'tstamp',
-					1 => 'input_1(200)',
-					2 => 'input_2(100)',
-					3 => 'endtime',
-				)
-			)
-		);
-
-		return array(
-			'test1' => array($testSql, $expected)
-		);
-	}
-
-	/**
-	 * @test
-	 * @dataProvider parseSQLDataProvider
-	 * @param string $sql The SQL to trim
-	 * @param array $expected The expected trimmed SQL with single space at the end
-	 */
-	public function parseSQL($sql, $expected) {
-		$result = $this->subject->_callRef('parseSQL', $sql);
-		$this->assertSame($expected, $result);
-	}
-}
diff --git a/typo3/sysext/dbal/Classes/Database/DatabaseConnection.php b/typo3/sysext/dbal/Classes/Database/DatabaseConnection.php
index 392b6f9dddd71e666bb5d356482a67d7c557e61c..51ec6ae442cf05dffd05b6749df32433df3b3985 100644
--- a/typo3/sysext/dbal/Classes/Database/DatabaseConnection.php
+++ b/typo3/sysext/dbal/Classes/Database/DatabaseConnection.php
@@ -145,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;
 
@@ -209,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:
@@ -936,7 +936,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()
 	 */
@@ -3417,7 +3417,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
@@ -3612,10 +3612,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()
diff --git a/typo3/sysext/dbal/Classes/Database/SqlCompilers/AbstractCompiler.php b/typo3/sysext/dbal/Classes/Database/SqlCompilers/AbstractCompiler.php
new file mode 100644
index 0000000000000000000000000000000000000000..100ec906513da164e3331401c6f431bfee715cc5
--- /dev/null
+++ b/typo3/sysext/dbal/Classes/Database/SqlCompilers/AbstractCompiler.php
@@ -0,0 +1,316 @@
+<?php
+namespace TYPO3\CMS\Dbal\Database\SqlCompilers;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Dbal\Database\DatabaseConnection;
+
+/**
+ * Abstract base class for SQL compilers
+ */
+abstract class AbstractCompiler {
+
+	/**
+	 * @var \TYPO3\CMS\Dbal\Database\DatabaseConnection
+	 */
+	protected $databaseConnection;
+
+	/**
+	 * @param \TYPO3\CMS\Dbal\Database\DatabaseConnection $databaseConnection
+	 */
+	public function __construct(DatabaseConnection $databaseConnection) {
+		$this->databaseConnection = $databaseConnection;
+	}
+
+	/**
+	 * Compiles an SQL query from components
+	 *
+	 * @param array $components Array of SQL query components
+	 * @return string SQL query
+	 * @see parseSQL()
+	 */
+	public function compileSQL($components) {
+		$query = '';
+		switch ($components['type']) {
+			case 'SELECT':
+				$query = $this->compileSELECT($components);
+				break;
+			case 'UPDATE':
+				$query = $this->compileUPDATE($components);
+				break;
+			case 'INSERT':
+				$query = $this->compileINSERT($components);
+				break;
+			case 'DELETE':
+				$query = $this->compileDELETE($components);
+				break;
+			case 'EXPLAIN':
+				$query = 'EXPLAIN ' . $this->compileSELECT($components);
+				break;
+			case 'DROPTABLE':
+				$query = 'DROP TABLE' . ($components['ifExists'] ? ' IF EXISTS' : '') . ' ' . $components['TABLE'];
+				break;
+			case 'CREATETABLE':
+				$query = $this->compileCREATETABLE($components);
+				break;
+			case 'ALTERTABLE':
+				$query = $this->compileALTERTABLE($components);
+				break;
+			case 'TRUNCATETABLE':
+				$query = $this->compileTRUNCATETABLE($components);
+				break;
+		}
+		return $query;
+	}
+
+	/**
+	 * Compiles a SELECT statement from components array
+	 *
+	 * @param array $components Array of SQL query components
+	 * @return string SQL SELECT query
+	 * @see parseSELECT()
+	 */
+	protected function compileSELECT($components) {
+		// Initialize:
+		$where = $this->compileWhereClause($components['WHERE']);
+		$groupBy = $this->compileFieldList($components['GROUPBY']);
+		$orderBy = $this->compileFieldList($components['ORDERBY']);
+		$limit = $components['LIMIT'];
+		// Make query:
+		$query = 'SELECT ' . ($components['STRAIGHT_JOIN'] ?: '') . ' ' .
+			$this->compileFieldList($components['SELECT']) .
+			' FROM ' . $this->compileFromTables($components['FROM']) . ($where !== '' ?
+				' WHERE ' . $where : '') . ($groupBy !== '' ?
+				' GROUP BY ' . $groupBy : '') . ($orderBy !== '' ?
+				' ORDER BY ' . $orderBy : '') . ((string)$limit !== '' ?
+				' LIMIT ' . $limit : '');
+		return $query;
+	}
+
+	/**
+	 * Compiles an UPDATE statement from components array
+	 *
+	 * @param array $components Array of SQL query components
+	 * @return string SQL UPDATE query
+	 * @see parseUPDATE()
+	 */
+	protected function compileUPDATE($components) {
+		// Where clause:
+		$where = $this->compileWhereClause($components['WHERE']);
+		// Fields
+		$fields = array();
+		foreach ($components['FIELDS'] as $fN => $fV) {
+			$fields[] = $fN . '=' . $fV[1] . $this->compileAddslashes($fV[0]) . $fV[1];
+		}
+		// Make query:
+		$query = 'UPDATE ' . $components['TABLE'] . ' SET ' . implode(',', $fields) .
+			($where !== '' ? ' WHERE ' . $where : '');
+
+		return $query;
+	}
+
+	/**
+	 * Compiles an INSERT statement from components array
+	 *
+	 * @param array $components Array of SQL query components
+	 * @return string SQL INSERT query
+	 * @see parseINSERT()
+	 */
+	abstract protected function compileINSERT($components);
+
+	/**
+	 * Compiles an DELETE statement from components array
+	 *
+	 * @param array $components Array of SQL query components
+	 * @return string SQL DELETE query
+	 * @see parseDELETE()
+	 */
+	protected function compileDELETE($components) {
+		// Where clause:
+		$where = $this->compileWhereClause($components['WHERE']);
+		// Make query:
+		$query = 'DELETE FROM ' . $components['TABLE'] . ($where !== '' ? ' WHERE ' . $where : '');
+
+		return $query;
+	}
+
+	/**
+	 * Compiles a CREATE TABLE statement from components array
+	 *
+	 * @param array $components Array of SQL query components
+	 * @return array array with SQL CREATE TABLE/INDEX command(s)
+	 * @see parseCREATETABLE()
+	 */
+	abstract protected function compileCREATETABLE($components);
+
+	/**
+	 * Compiles an ALTER TABLE statement from components array
+	 *
+	 * @param array Array of SQL query components
+	 * @return string SQL ALTER TABLE query
+	 * @see parseALTERTABLE()
+	 */
+	abstract protected function compileALTERTABLE($components);
+
+	/**
+	 * Compiles a TRUNCATE TABLE statement from components array
+	 *
+	 * @param array $components Array of SQL query components
+	 * @return string SQL TRUNCATE TABLE query
+	 * @see parseTRUNCATETABLE()
+	 */
+	protected function compileTRUNCATETABLE(array $components) {
+		// Make query:
+		$query = 'TRUNCATE TABLE ' . $components['TABLE'];
+		// Return query
+		return $query;
+	}
+
+	/**
+	 * Compiles a "SELECT [output] FROM..:" field list based on input array (made with ->parseFieldList())
+	 * Can also compile field lists for ORDER BY and GROUP BY.
+	 *
+	 * @param array $selectFields Array of select fields, (made with ->parseFieldList())
+	 * @param bool $compileComments Whether comments should be compiled
+	 * @param bool $functionMapping Whether function mapping should take place
+	 * @return string Select field string
+	 * @see parseFieldList()
+	 */
+	abstract public function compileFieldList($selectFields, $compileComments = TRUE, $functionMapping = TRUE);
+
+	/**
+	 * Implodes an array of WHERE clause configuration into a WHERE clause.
+	 *
+	 * DBAL-specific: The only(!) handled "calc" operators supported by parseWhereClause() are:
+	 * - the bitwise logical and (&)
+	 * - the addition (+)
+	 * - the substraction (-)
+	 * - the multiplication (*)
+	 * - the division (/)
+	 * - the modulo (%)
+	 *
+	 * @param array $clauseArray
+	 * @param bool $functionMapping
+	 * @return string WHERE clause as string.
+	 * @see \TYPO3\CMS\Core\Database\SqlParser::parseWhereClause()
+	 */
+	abstract public function compileWhereClause($clauseArray, $functionMapping = TRUE);
+
+	/**
+	 * Add slashes function used for compiling queries
+	 * This method overrides the method from \TYPO3\CMS\Dbal\Database\NativeSqlParser because
+	 * the input string is already properly escaped.
+	 *
+	 * @param string $str Input string
+	 * @return string Output string
+	 */
+	abstract protected function compileAddslashes($str);
+
+	/**
+	 * Compile a "JOIN table ON [output] = ..." identifier
+	 *
+	 * @param array $identifierParts Array of identifier parts
+	 * @return string
+	 * @see parseCastStatement()
+	 * @see parseFromTables()
+	 */
+	protected function compileJoinIdentifier($identifierParts) {
+		if ($identifierParts['type'] === 'cast') {
+			return sprintf('CAST(%s AS %s)',
+				$identifierParts['table'] ? $identifierParts['table'] . '.' . $identifierParts['field'] : $identifierParts['field'],
+				$identifierParts['datatype'][0]
+			);
+		} else {
+			return $identifierParts['table'] ? $identifierParts['table'] . '.' . $identifierParts['field'] : $identifierParts['field'];
+		}
+	}
+
+	/**
+	 * Compiles a "FROM [output] WHERE..:" table list based on input array (made with ->parseFromTables())
+	 *
+	 * @param array $tablesArray Array of table names, (made with ->parseFromTables())
+	 * @return string Table name string
+	 * @see parseFromTables()
+	 */
+	public function compileFromTables($tablesArray) {
+		// Prepare buffer variable:
+		$outputParts = array();
+		// Traverse the table names:
+		if (is_array($tablesArray)) {
+			foreach ($tablesArray as $k => $v) {
+				// Set table name:
+				$outputParts[$k] = $v['table'];
+				// Add alias AS if there:
+				if ($v['as']) {
+					$outputParts[$k] .= ' ' . $v['as_keyword'] . ' ' . $v['as'];
+				}
+				if (is_array($v['JOIN'])) {
+					foreach ($v['JOIN'] as $join) {
+						$outputParts[$k] .= ' ' . $join['type'] . ' ' . $join['withTable'];
+						// Add alias AS if there:
+						if (isset($join['as']) && $join['as']) {
+							$outputParts[$k] .= ' ' . $join['as_keyword'] . ' ' . $join['as'];
+						}
+						$outputParts[$k] .= ' ON ';
+						foreach ($join['ON'] as $condition) {
+							if ($condition['operator'] !== '') {
+								$outputParts[$k] .= ' ' . $condition['operator'] . ' ';
+							}
+							$outputParts[$k] .= $this->compileJoinIdentifier($condition['left']);
+							$outputParts[$k] .= $condition['comparator'];
+							if (!empty($condition['right']['value'])) {
+								$value = $condition['right']['value'];
+								$outputParts[$k] .= $value[1] . $this->compileAddslashes($value[0]) . $value[1];
+							} else {
+								$outputParts[$k] .= $this->compileJoinIdentifier($condition['right']);
+							}
+						}
+					}
+				}
+			}
+		}
+		// Return imploded buffer:
+		return implode(', ', $outputParts);
+	}
+
+	/**
+	 * Compiles a CASE ... WHEN flow-control construct based on input array (made with ->parseCaseStatement())
+	 *
+	 * @param array $components Array of case components, (made with ->parseCaseStatement())
+	 * @param bool $functionMapping Whether function mapping should take place
+	 * @return string case when string
+	 * @see parseCaseStatement()
+	 */
+	protected function compileCaseStatement(array $components, $functionMapping = TRUE) {
+		$statement = 'CASE';
+		if (isset($components['case_field'])) {
+			$statement .= ' ' . $components['case_field'];
+		} elseif (isset($components['case_value'])) {
+			$statement .= ' ' . $components['case_value'][1] . $components['case_value'][0] . $components['case_value'][1];
+		}
+		foreach ($components['when'] as $when) {
+			$statement .= ' WHEN ';
+			$statement .= $this->compileWhereClause($when['when_value'], $functionMapping);
+			$statement .= ' THEN ';
+			$statement .= $when['then_value'][1] . $when['then_value'][0] . $when['then_value'][1];
+		}
+		if (isset($components['else'])) {
+			$statement .= ' ELSE ';
+			$statement .= $components['else'][1] . $components['else'][0] . $components['else'][1];
+		}
+		$statement .= ' END';
+		return $statement;
+	}
+
+}
diff --git a/typo3/sysext/dbal/Classes/Database/SqlCompilers/Adodb.php b/typo3/sysext/dbal/Classes/Database/SqlCompilers/Adodb.php
new file mode 100644
index 0000000000000000000000000000000000000000..7bb9bda13b2869db239a62bfacdcf780c504dc04
--- /dev/null
+++ b/typo3/sysext/dbal/Classes/Database/SqlCompilers/Adodb.php
@@ -0,0 +1,590 @@
+<?php
+namespace TYPO3\CMS\Dbal\Database\SqlCompilers;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Dbal\Database\Specifics;
+use TYPO3\CMS\Dbal\Database\SqlParser;
+
+/**
+ * SQL Compiler for ADOdb connections
+ */
+class Adodb extends AbstractCompiler {
+	/**
+	 * Compiles an INSERT statement from components array
+	 *
+	 * @param array Array of SQL query components
+	 * @return string SQL INSERT query / array
+	 * @see parseINSERT()
+	 */
+	protected function compileINSERT($components) {
+		$values = array();
+		if (isset($components['VALUES_ONLY']) && is_array($components['VALUES_ONLY'])) {
+			$valuesComponents = $components['EXTENDED'] === '1' ? $components['VALUES_ONLY'] : array($components['VALUES_ONLY']);
+			$tableFields = array_keys($this->databaseConnection->cache_fieldType[$components['TABLE']]);
+		} else {
+			$valuesComponents = $components['EXTENDED'] === '1' ? $components['FIELDS'] : array($components['FIELDS']);
+			$tableFields = array_keys($valuesComponents[0]);
+		}
+		foreach ($valuesComponents as $valuesComponent) {
+			$fields = array();
+			$fc = 0;
+			foreach ($valuesComponent as $fV) {
+				$fields[$tableFields[$fc++]] = $fV[0];
+			}
+			$values[] = $fields;
+		}
+		return count($values) === 1 ? $values[0] : $values;
+	}
+
+	/**
+	 * Compiles a CREATE TABLE statement from components array
+	 *
+	 * @param array $components Array of SQL query components
+	 * @return array array with SQL CREATE TABLE/INDEX command(s)
+	 * @see parseCREATETABLE()
+	 */
+	protected function compileCREATETABLE($components) {
+		// Create fields and keys:
+		$fieldsKeys = array();
+		$indexKeys = array();
+		foreach ($components['FIELDS'] as $fN => $fCfg) {
+			$handlerKey = $this->databaseConnection->handler_getFromTableList($components['TABLE']);
+			$fieldsKeys[$fN] = $this->databaseConnection->quoteName($fN, $handlerKey, TRUE) . ' ' . $this->compileFieldCfg($fCfg['definition']);
+		}
+		if (isset($components['KEYS']) && is_array($components['KEYS'])) {
+			foreach ($components['KEYS'] as $kN => $kCfg) {
+				if ($kN === 'PRIMARYKEY') {
+					foreach ($kCfg as $field) {
+						$fieldsKeys[$field] .= ' PRIMARY';
+					}
+				} elseif ($kN === 'UNIQUE') {
+					foreach ($kCfg as $n => $field) {
+						$indexKeys = array_merge($indexKeys, $this->compileCREATEINDEX($n, $components['TABLE'], $field, array('UNIQUE')));
+					}
+				} else {
+					$indexKeys = array_merge($indexKeys, $this->compileCREATEINDEX($kN, $components['TABLE'], $kCfg));
+				}
+			}
+		}
+		// Generally create without OID on PostgreSQL
+		$tableOptions = array('postgres' => 'WITHOUT OIDS');
+		// Fetch table/index generation query:
+		$tableName = $this->databaseConnection->quoteName($components['TABLE'], NULL, TRUE);
+		$query = array_merge($this->databaseConnection->handlerInstance[$this->databaseConnection->lastHandlerKey]->DataDictionary->CreateTableSQL($tableName, implode(',' . LF, $fieldsKeys), $tableOptions), $indexKeys);
+		return $query;
+	}
+
+	/**
+	 * Compiles an ALTER TABLE statement from components array
+	 *
+	 * @param array Array of SQL query components
+	 * @return string SQL ALTER TABLE query
+	 * @see parseALTERTABLE()
+	 */
+	protected function compileALTERTABLE($components) {
+		$query = '';
+		$tableName = $this->databaseConnection->quoteName($components['TABLE'], NULL, TRUE);
+		$fieldName = $this->databaseConnection->quoteName($components['FIELD'], NULL, TRUE);
+		switch (strtoupper(str_replace(array(' ', "\n", "\r", "\t"), '', $components['action']))) {
+			case 'ADD':
+				$query = $this->databaseConnection->handlerInstance[$this->databaseConnection->lastHandlerKey]->DataDictionary->AddColumnSQL($tableName, $fieldName . ' ' . $this->compileFieldCfg($components['definition']));
+				break;
+			case 'CHANGE':
+				$query = $this->databaseConnection->handlerInstance[$this->databaseConnection->lastHandlerKey]->DataDictionary->AlterColumnSQL($tableName, $fieldName . ' ' . $this->compileFieldCfg($components['definition']));
+				break;
+			case 'DROP':
+
+			case 'DROPKEY':
+				$query = $this->compileDROPINDEX($components['KEY'], $components['TABLE']);
+				break;
+
+			case 'ADDKEY':
+				$query = $this->compileCREATEINDEX($components['KEY'], $components['TABLE'], $components['fields']);
+				break;
+			case 'ADDUNIQUE':
+				$query = $this->compileCREATEINDEX($components['KEY'], $components['TABLE'], $components['fields'], array('UNIQUE'));
+				break;
+			case 'ADDPRIMARYKEY':
+				// @todo ???
+				break;
+			case 'DEFAULTCHARACTERSET':
+
+			case 'ENGINE':
+				// @todo ???
+				break;
+		}
+		return $query;
+	}
+
+	/**
+	 * Compiles CREATE INDEX statements from component information
+	 *
+	 * MySQL only needs uniqueness of index names per table, but many DBMS require uniqueness of index names per schema.
+	 * The table name is hashed and prepended to the index name to make sure index names are unique.
+	 *
+	 * @param string $indexName
+	 * @param string $tableName
+	 * @param array $indexFields
+	 * @param array $indexOptions
+	 * @return array
+	 * @see compileALTERTABLE()
+	 */
+	protected function compileCREATEINDEX($indexName, $tableName, $indexFields, $indexOptions = array()) {
+		$indexIdentifier = $this->databaseConnection->quoteName(hash('crc32b', $tableName) . '_' . $indexName, NULL, TRUE);
+		$dbmsSpecifics = $this->databaseConnection->getSpecifics();
+		$keepFieldLengths = $dbmsSpecifics->specificExists(Specifics\AbstractSpecifics::PARTIAL_STRING_INDEX) && $dbmsSpecifics->getSpecific(Specifics\AbstractSpecifics::PARTIAL_STRING_INDEX);
+
+		foreach ($indexFields as $key => $fieldName) {
+			if (!$keepFieldLengths) {
+				$fieldName = preg_replace('/\A([^\(]+)(\(\d+\))/', '\\1', $fieldName);
+			}
+			// Quote the fieldName in backticks with escaping, ADOdb will replace the backticks with the correct quoting
+			$indexFields[$key] = '`' . str_replace('`', '``', $fieldName) . '`';
+		}
+
+		return $this->databaseConnection->handlerInstance[$this->databaseConnection->handler_getFromTableList($tableName)]->DataDictionary->CreateIndexSQL(
+			$indexIdentifier, $this->databaseConnection->quoteName($tableName, NULL, TRUE), $indexFields, $indexOptions
+		);
+	}
+
+	/**
+	 * Compiles DROP INDEX statements from component information
+	 *
+	 * MySQL only needs uniqueness of index names per table, but many DBMS require uniqueness of index names per schema.
+	 * The table name is hashed and prepended to the index name to make sure index names are unique.
+	 *
+	 * @param $indexName
+	 * @param $tableName
+	 * @return array
+	 * @see compileALTERTABLE()
+	 */
+	protected function compileDROPINDEX($indexName, $tableName) {
+		$indexIdentifier = $this->databaseConnection->quoteName(hash('crc32b', $tableName) . '_' . $indexName, NULL, TRUE);
+
+		return $this->databaseConnection->handlerInstance[$this->databaseConnection->handler_getFromTableList($tableName)]->DataDictionary->DropIndexSQL(
+			$indexIdentifier, $this->databaseConnection->quoteName($tableName)
+		);
+	}
+
+	/**
+	 * Compiles a "SELECT [output] FROM..:" field list based on input array (made with ->parseFieldList())
+	 * Can also compile field lists for ORDER BY and GROUP BY.
+	 *
+	 * @param array $selectFields Array of select fields, (made with ->parseFieldList())
+	 * @param bool $compileComments Whether comments should be compiled
+	 * @param bool $functionMapping Whether function mapping should take place
+	 * @return string Select field string
+	 * @see parseFieldList()
+	 */
+	public function compileFieldList($selectFields, $compileComments = TRUE, $functionMapping = TRUE) {
+		$output = '';
+		// Traverse the selectFields if any:
+		if (is_array($selectFields)) {
+			$outputParts = array();
+			foreach ($selectFields as $k => $v) {
+				// Detecting type:
+				switch ($v['type']) {
+					case 'function':
+						$outputParts[$k] = $v['function'] . '(' . $v['func_content'] . ')';
+						break;
+					case 'flow-control':
+						if ($v['flow-control']['type'] === 'CASE') {
+							$outputParts[$k] = $this->compileCaseStatement($v['flow-control'], $functionMapping);
+						}
+						break;
+					case 'field':
+						$outputParts[$k] = ($v['distinct'] ? $v['distinct'] : '') . ($v['table'] ? $v['table'] . '.' : '') . $v['field'];
+						break;
+				}
+				// Alias:
+				if ($v['as']) {
+					$outputParts[$k] .= ' ' . $v['as_keyword'] . ' ' . $v['as'];
+				}
+				// Specifically for ORDER BY and GROUP BY field lists:
+				if ($v['sortDir']) {
+					$outputParts[$k] .= ' ' . $v['sortDir'];
+				}
+			}
+			// @todo Handle SQL hints in comments according to current DBMS
+			if (FALSE && $selectFields[0]['comments']) {
+				$output = $selectFields[0]['comments'] . ' ';
+			}
+			$output .= implode(', ', $outputParts);
+		}
+		return $output;
+	}
+
+	/**
+	 * Add slashes function used for compiling queries
+	 * This method overrides the method from \TYPO3\CMS\Dbal\Database\NativeSqlParser because
+	 * the input string is already properly escaped.
+	 *
+	 * @param string $str Input string
+	 * @return string Output string
+	 */
+	protected function compileAddslashes($str) {
+		return $str;
+	}
+
+	/**
+	 * Compile field definition
+	 *
+	 * @param array $fieldCfg Field definition parts
+	 * @return string Field definition string
+	 */
+	protected function compileFieldCfg($fieldCfg) {
+		// Set type:
+		$type = $this->databaseConnection->getSpecifics()->getMetaFieldType($fieldCfg['fieldType']);
+		$cfg = $type;
+		// Add value, if any:
+		if ((string)$fieldCfg['value'] !== '' && in_array($type, array('C', 'C2'))) {
+			$cfg .= ' ' . $fieldCfg['value'];
+		} elseif (!isset($fieldCfg['value']) && in_array($type, array('C', 'C2'))) {
+			$cfg .= ' 255';
+		}
+		// Add additional features:
+		$noQuote = TRUE;
+		if (is_array($fieldCfg['featureIndex'])) {
+			// MySQL assigns DEFAULT value automatically if NOT NULL, fake this here
+			// numeric fields get 0 as default, other fields an empty string
+			if (isset($fieldCfg['featureIndex']['NOTNULL']) && !isset($fieldCfg['featureIndex']['DEFAULT']) && !isset($fieldCfg['featureIndex']['AUTO_INCREMENT'])) {
+				switch ($type) {
+					case 'I8':
+
+					case 'F':
+
+					case 'N':
+						$fieldCfg['featureIndex']['DEFAULT'] = array('keyword' => 'DEFAULT', 'value' => array('0', ''));
+						break;
+					default:
+						$fieldCfg['featureIndex']['DEFAULT'] = array('keyword' => 'DEFAULT', 'value' => array('', '\''));
+				}
+			}
+			foreach ($fieldCfg['featureIndex'] as $feature => $featureDef) {
+				switch (TRUE) {
+					case $feature === 'UNSIGNED' && !$this->databaseConnection->runningADOdbDriver('mysql'):
+					case $feature === 'NOTNULL' && $this->databaseConnection->runningADOdbDriver('oci8'):
+						continue;
+					case $feature === 'AUTO_INCREMENT':
+						$cfg .= ' AUTOINCREMENT';
+						break;
+					case $feature === 'NOTNULL':
+						$cfg .= ' NOTNULL';
+						break;
+					default:
+						$cfg .= ' ' . $featureDef['keyword'];
+				}
+				// Add value if found:
+				if (is_array($featureDef['value'])) {
+					if ($featureDef['value'][0] === '') {
+						$cfg .= ' "\'\'"';
+					} else {
+						$cfg .= ' ' . $featureDef['value'][1] . $this->compileAddslashes($featureDef['value'][0]) . $featureDef['value'][1];
+						if (!is_numeric($featureDef['value'][0])) {
+							$noQuote = FALSE;
+						}
+					}
+				}
+			}
+		}
+		if ($noQuote) {
+			$cfg .= ' NOQUOTE';
+		}
+		// Return field definition string:
+		return $cfg;
+	}
+
+	/**
+	 * Implodes an array of WHERE clause configuration into a WHERE clause.
+	 *
+	 * DBAL-specific: The only(!) handled "calc" operators supported by parseWhereClause() are:
+	 * - the bitwise logical and (&)
+	 * - the addition (+)
+	 * - the substraction (-)
+	 * - the multiplication (*)
+	 * - the division (/)
+	 * - the modulo (%)
+	 *
+	 * @param array $clauseArray
+	 * @param bool $functionMapping
+	 * @return string WHERE clause as string.
+	 * @see \TYPO3\CMS\Core\Database\SqlParser::parseWhereClause()
+	 */
+	public function compileWhereClause($clauseArray, $functionMapping = TRUE) {
+		// Prepare buffer variable:
+		$output = '';
+		// Traverse clause array:
+		if (is_array($clauseArray)) {
+			foreach ($clauseArray as $v) {
+				// Set operator:
+				$output .= $v['operator'] ? ' ' . $v['operator'] : '';
+				// Look for sublevel:
+				if (is_array($v['sub'])) {
+					$output .= ' (' . trim($this->compileWhereClause($v['sub'], $functionMapping)) . ')';
+				} elseif (isset($v['func']) && $v['func']['type'] === 'EXISTS') {
+					$output .= ' ' . trim($v['modifier']) . ' EXISTS (' . $this->compileSELECT($v['func']['subquery']) . ')';
+				} else {
+					if (isset($v['func']) && $v['func']['type'] === 'LOCATE') {
+						$output .= ' ' . trim($v['modifier']);
+						switch (TRUE) {
+							case $this->databaseConnection->runningADOdbDriver('mssql') && $functionMapping:
+								$output .= ' CHARINDEX(';
+								$output .= $v['func']['substr'][1] . $v['func']['substr'][0] . $v['func']['substr'][1];
+								$output .= ', ' . ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
+								$output .= isset($v['func']['pos']) ? ', ' . $v['func']['pos'][0] : '';
+								$output .= ')';
+								break;
+							case $this->databaseConnection->runningADOdbDriver('oci8') && $functionMapping:
+								$output .= ' INSTR(';
+								$output .= ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
+								$output .= ', ' . $v['func']['substr'][1] . $v['func']['substr'][0] . $v['func']['substr'][1];
+								$output .= isset($v['func']['pos']) ? ', ' . $v['func']['pos'][0] : '';
+								$output .= ')';
+								break;
+							default:
+								$output .= ' LOCATE(';
+								$output .= $v['func']['substr'][1] . $v['func']['substr'][0] . $v['func']['substr'][1];
+								$output .= ', ' . ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
+								$output .= isset($v['func']['pos']) ? ', ' . $v['func']['pos'][0] : '';
+								$output .= ')';
+						}
+					} elseif (isset($v['func']) && $v['func']['type'] === 'IFNULL') {
+						$output .= ' ' . trim($v['modifier']) . ' ';
+						switch (TRUE) {
+							case $this->databaseConnection->runningADOdbDriver('mssql') && $functionMapping:
+								$output .= 'ISNULL';
+								break;
+							case $this->databaseConnection->runningADOdbDriver('oci8') && $functionMapping:
+								$output .= 'NVL';
+								break;
+							default:
+								$output .= 'IFNULL';
+						}
+						$output .= '(';
+						$output .= ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
+						$output .= ', ' . $v['func']['default'][1] . $this->compileAddslashes($v['func']['default'][0]) . $v['func']['default'][1];
+						$output .= ')';
+					} elseif (isset($v['func']) && $v['func']['type'] === 'FIND_IN_SET') {
+						$output .= ' ' . trim($v['modifier']) . ' ';
+						if ($functionMapping) {
+							switch (TRUE) {
+								case $this->databaseConnection->runningADOdbDriver('mssql'):
+									$field = ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
+									if (!isset($v['func']['str_like'])) {
+										$v['func']['str_like'] = $v['func']['str'][0];
+									}
+									$output .= '\',\'+' . $field . '+\',\' LIKE \'%,' . $v['func']['str_like'] . ',%\'';
+									break;
+								case $this->databaseConnection->runningADOdbDriver('oci8'):
+									$field = ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
+									if (!isset($v['func']['str_like'])) {
+										$v['func']['str_like'] = $v['func']['str'][0];
+									}
+									$output .= '\',\'||' . $field . '||\',\' LIKE \'%,' . $v['func']['str_like'] . ',%\'';
+									break;
+								case $this->databaseConnection->runningADOdbDriver('postgres'):
+									$output .= ' FIND_IN_SET(';
+									$output .= $v['func']['str'][1] . $v['func']['str'][0] . $v['func']['str'][1];
+									$output .= ', ' . ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
+									$output .= ') != 0';
+									break;
+								default:
+									$field = ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
+									if (!isset($v['func']['str_like'])) {
+										$v['func']['str_like'] = $v['func']['str'][0];
+									}
+									$output .= '(' . $field . ' LIKE \'%,' . $v['func']['str_like'] . ',%\'' . ' OR ' . $field . ' LIKE \'' . $v['func']['str_like'] . ',%\'' . ' OR ' . $field . ' LIKE \'%,' . $v['func']['str_like'] . '\'' . ' OR ' . $field . '= ' . $v['func']['str'][1] . $v['func']['str'][0] . $v['func']['str'][1] . ')';
+							}
+						} else {
+							switch (TRUE) {
+								case $this->databaseConnection->runningADOdbDriver('mssql'):
+
+								case $this->databaseConnection->runningADOdbDriver('oci8'):
+
+								case $this->databaseConnection->runningADOdbDriver('postgres'):
+									$output .= ' FIND_IN_SET(';
+									$output .= $v['func']['str'][1] . $v['func']['str'][0] . $v['func']['str'][1];
+									$output .= ', ' . ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
+									$output .= ')';
+									break;
+								default:
+									$field = ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
+									if (!isset($v['func']['str_like'])) {
+										$v['func']['str_like'] = $v['func']['str'][0];
+									}
+									$output .= '(' . $field . ' LIKE \'%,' . $v['func']['str_like'] . ',%\'' . ' OR ' . $field . ' LIKE \'' . $v['func']['str_like'] . ',%\'' . ' OR ' . $field . ' LIKE \'%,' . $v['func']['str_like'] . '\'' . ' OR ' . $field . '= ' . $v['func']['str'][1] . $v['func']['str'][0] . $v['func']['str'][1] . ')';
+							}
+						}
+					} else {
+						// Set field/table with modifying prefix if any:
+						$output .= ' ' . trim($v['modifier']) . ' ';
+						// DBAL-specific: Set calculation, if any:
+						if ($v['calc'] === '&' && $functionMapping) {
+							switch (TRUE) {
+								case $this->databaseConnection->runningADOdbDriver('oci8'):
+									// Oracle only knows BITAND(x,y) - sigh
+									$output .= 'BITAND(' . trim((($v['table'] ? $v['table'] . '.' : '') . $v['field'])) . ',' . $v['calc_value'][1] . $this->compileAddslashes($v['calc_value'][0]) . $v['calc_value'][1] . ')';
+									break;
+								default:
+									// MySQL, MS SQL Server, PostgreSQL support the &-syntax
+									$output .= trim((($v['table'] ? $v['table'] . '.' : '') . $v['field'])) . $v['calc'] . $v['calc_value'][1] . $this->compileAddslashes($v['calc_value'][0]) . $v['calc_value'][1];
+							}
+						} elseif ($v['calc']) {
+							$output .= trim((($v['table'] ? $v['table'] . '.' : '') . $v['field'])) . $v['calc'];
+							if (isset($v['calc_table'])) {
+								$output .= trim(($v['calc_table'] ? $v['calc_table'] . '.' : '') . $v['calc_field']);
+							} else {
+								$output .= $v['calc_value'][1] . $this->compileAddslashes($v['calc_value'][0]) . $v['calc_value'][1];
+							}
+						} elseif (!($this->databaseConnection->runningADOdbDriver('oci8') && preg_match('/(NOT )?LIKE( BINARY)?/', $v['comparator']) && $functionMapping)) {
+							$output .= trim(($v['table'] ? $v['table'] . '.' : '') . $v['field']);
+						}
+					}
+					// Set comparator:
+					if ($v['comparator']) {
+						$isLikeOperator = preg_match('/(NOT )?LIKE( BINARY)?/', $v['comparator']);
+						switch (TRUE) {
+							case $this->databaseConnection->runningADOdbDriver('oci8') && $isLikeOperator && $functionMapping:
+								// Oracle cannot handle LIKE on CLOB fields - sigh
+								if (isset($v['value']['operator'])) {
+									$values = array();
+									foreach ($v['value']['args'] as $fieldDef) {
+										$values[] = ($fieldDef['table'] ? $fieldDef['table'] . '.' : '') . $fieldDef['field'];
+									}
+									$compareValue = ' ' . $v['value']['operator'] . '(' . implode(',', $values) . ')';
+								} else {
+									$compareValue = $v['value'][1] . $this->compileAddslashes(trim($v['value'][0], '%')) . $v['value'][1];
+								}
+								if (GeneralUtility::isFirstPartOfStr($v['comparator'], 'NOT')) {
+									$output .= 'NOT ';
+								}
+								// To be on the safe side
+								$isLob = TRUE;
+								if ($v['table']) {
+									// Table and field names are quoted:
+									$tableName = substr($v['table'], 1, strlen($v['table']) - 2);
+									$fieldName = substr($v['field'], 1, strlen($v['field']) - 2);
+									$fieldType = $this->databaseConnection->sql_field_metatype($tableName, $fieldName);
+									$isLob = $fieldType === 'B' || $fieldType === 'XL';
+								}
+								if (strtoupper(substr($v['comparator'], -6)) === 'BINARY') {
+									if ($isLob) {
+										$output .= '(dbms_lob.instr(' . trim((($v['table'] ? $v['table'] . '.' : '') . $v['field'])) . ', ' . $compareValue . ',1,1) > 0)';
+									} else {
+										$output .= '(instr(' . trim((($v['table'] ? $v['table'] . '.' : '') . $v['field'])) . ', ' . $compareValue . ',1,1) > 0)';
+									}
+								} else {
+									if ($isLob) {
+										$output .= '(dbms_lob.instr(LOWER(' . trim((($v['table'] ? $v['table'] . '.' : '') . $v['field'])) . '), ' . GeneralUtility::strtolower($compareValue) . ',1,1) > 0)';
+									} else {
+										$output .= '(instr(LOWER(' . trim((($v['table'] ? $v['table'] . '.' : '') . $v['field'])) . '), ' . GeneralUtility::strtolower($compareValue) . ',1,1) > 0)';
+									}
+								}
+								break;
+							default:
+								if ($isLikeOperator && $functionMapping) {
+									if ($this->databaseConnection->runningADOdbDriver('postgres') || $this->databaseConnection->runningADOdbDriver('postgres64') || $this->databaseConnection->runningADOdbDriver('postgres7') || $this->databaseConnection->runningADOdbDriver('postgres8')) {
+										// Remap (NOT)? LIKE to (NOT)? ILIKE
+										// and (NOT)? LIKE BINARY to (NOT)? LIKE
+										switch ($v['comparator']) {
+											case 'LIKE':
+												$v['comparator'] = 'ILIKE';
+												break;
+											case 'NOT LIKE':
+												$v['comparator'] = 'NOT ILIKE';
+												break;
+											default:
+												$v['comparator'] = str_replace(' BINARY', '', $v['comparator']);
+										}
+									} else {
+										// No more BINARY operator
+										$v['comparator'] = str_replace(' BINARY', '', $v['comparator']);
+									}
+								}
+								$output .= ' ' . $v['comparator'];
+								// Detecting value type; list or plain:
+								$comparator = SqlParser::normalizeKeyword($v['comparator']);
+								if (GeneralUtility::inList('NOTIN,IN', $comparator)) {
+									if (isset($v['subquery'])) {
+										$output .= ' (' . $this->compileSELECT($v['subquery']) . ')';
+									} else {
+										$valueBuffer = array();
+										foreach ($v['value'] as $realValue) {
+											$valueBuffer[] = $realValue[1] . $this->compileAddslashes($realValue[0]) . $realValue[1];
+										}
+
+										$dbmsSpecifics = $this->databaseConnection->getSpecifics();
+										if ($dbmsSpecifics === NULL) {
+											$output .= ' (' . trim(implode(',', $valueBuffer)) . ')';
+										} else {
+											$chunkedList = $dbmsSpecifics->splitMaxExpressions($valueBuffer);
+											$chunkCount = count($chunkedList);
+
+											if ($chunkCount === 1) {
+												$output .= ' (' . trim(implode(',', $valueBuffer)) . ')';
+											} else {
+												$listExpressions = array();
+												$field = trim(($v['table'] ? $v['table'] . '.' : '') . $v['field']);
+
+												switch ($comparator) {
+													case 'IN':
+														$operator = 'OR';
+														break;
+													case 'NOTIN':
+														$operator = 'AND';
+														break;
+													default:
+														$operator = '';
+												}
+
+												for ($i = 0; $i < $chunkCount; ++$i) {
+													$listPart = trim(implode(',', $chunkedList[$i]));
+													$listExpressions[] = ' (' . $listPart . ')';
+												}
+
+												$implodeString = ' ' . $operator . ' ' . $field . ' ' . $v['comparator'];
+
+												// add opening brace before field
+												$lastFieldPos = strrpos($output, $field);
+												$output = substr_replace($output, '(', $lastFieldPos, 0);
+												$output .= implode($implodeString, $listExpressions) . ')';
+											}
+										}
+									}
+								} elseif (GeneralUtility::inList('BETWEEN,NOT BETWEEN', $v['comparator'])) {
+									$lbound = $v['values'][0];
+									$ubound = $v['values'][1];
+									$output .= ' ' . $lbound[1] . $this->compileAddslashes($lbound[0]) . $lbound[1];
+									$output .= ' AND ';
+									$output .= $ubound[1] . $this->compileAddslashes($ubound[0]) . $ubound[1];
+								} elseif (isset($v['value']['operator'])) {
+									$values = array();
+									foreach ($v['value']['args'] as $fieldDef) {
+										$values[] = ($fieldDef['table'] ? $fieldDef['table'] . '.' : '') . $fieldDef['field'];
+									}
+									$output .= ' ' . $v['value']['operator'] . '(' . implode(',', $values) . ')';
+								} else {
+									$output .= ' ' . $v['value'][1] . $this->compileAddslashes($v['value'][0]) . $v['value'][1];
+								}
+						}
+					}
+				}
+			}
+		}
+		return $output;
+	}
+
+}
diff --git a/typo3/sysext/dbal/Classes/Database/SqlCompilers/Mysql.php b/typo3/sysext/dbal/Classes/Database/SqlCompilers/Mysql.php
new file mode 100644
index 0000000000000000000000000000000000000000..08dbfb3bd54dcc51709e0dfe9b4a9cba37f7ce88
--- /dev/null
+++ b/typo3/sysext/dbal/Classes/Database/SqlCompilers/Mysql.php
@@ -0,0 +1,311 @@
+<?php
+namespace TYPO3\CMS\Dbal\Database\SqlCompilers;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Dbal\Database\SqlParser;
+
+/**
+ * SQL Compiler for native MySQL connections
+ */
+class Mysql extends AbstractCompiler {
+
+	/**
+	 * Compiles an INSERT statement from components array
+	 *
+	 * @param array $components Array of SQL query components
+	 * @return string SQL INSERT query
+	 * @see parseINSERT()
+	 */
+	protected function compileINSERT($components) {
+		$values = array();
+		if (isset($components['VALUES_ONLY']) && is_array($components['VALUES_ONLY'])) {
+			$valuesComponents = $components['EXTENDED'] === '1' ? $components['VALUES_ONLY'] : array($components['VALUES_ONLY']);
+			$tableFields = array();
+		} else {
+			$valuesComponents = $components['EXTENDED'] === '1' ? $components['FIELDS'] : array($components['FIELDS']);
+			$tableFields = array_keys($valuesComponents[0]);
+		}
+		foreach ($valuesComponents as $valuesComponent) {
+			$fields = array();
+			foreach ($valuesComponent as $fV) {
+				$fields[] = $fV[1] . $this->compileAddslashes($fV[0]) . $fV[1];
+			}
+			$values[] = '(' . implode(',', $fields) . ')';
+		}
+		// Make query:
+		$query = 'INSERT INTO ' . $components['TABLE'];
+		if (!empty($tableFields)) {
+			$query .= ' (' . implode(',', $tableFields) . ')';
+		}
+		$query .= ' VALUES ' . implode(',', $values);
+
+		return $query;
+	}
+
+	/**
+	 * Compiles a CREATE TABLE statement from components array
+	 *
+	 * @param array $components Array of SQL query components
+	 * @return string SQL CREATE TABLE query
+	 * @see parseCREATETABLE()
+	 */
+	protected function compileCREATETABLE($components) {
+		// Create fields and keys:
+		$fieldsKeys = array();
+		foreach ($components['FIELDS'] as $fN => $fCfg) {
+			$fieldsKeys[] = $fN . ' ' . $this->compileFieldCfg($fCfg['definition']);
+		}
+		if ($components['KEYS']) {
+			foreach ($components['KEYS'] as $kN => $kCfg) {
+				if ($kN === 'PRIMARYKEY') {
+					$fieldsKeys[] = 'PRIMARY KEY (' . implode(',', $kCfg) . ')';
+				} elseif ($kN === 'UNIQUE') {
+					$key = key($kCfg);
+					$fields = current($kCfg);
+					$fieldsKeys[] = 'UNIQUE KEY ' . $key . ' (' . implode(',', $fields) . ')';
+				} else {
+					$fieldsKeys[] = 'KEY ' . $kN . ' (' . implode(',', $kCfg) . ')';
+				}
+			}
+		}
+		// Make query:
+		$query = 'CREATE TABLE ' . $components['TABLE'] . ' (' .
+			implode(',', $fieldsKeys) . ')' .
+			($components['engine'] ? ' ENGINE=' . $components['engine'] : '');
+
+		return $query;
+	}
+
+	/**
+	 * Compiles an ALTER TABLE statement from components array
+	 *
+	 * @param array $components Array of SQL query components
+	 * @return string SQL ALTER TABLE query
+	 * @see parseALTERTABLE()
+	 */
+	protected function compileALTERTABLE($components) {
+		// Make query:
+		$query = 'ALTER TABLE ' . $components['TABLE'] . ' ' . $components['action'] . ' ' . ($components['FIELD'] ?: $components['KEY']);
+		// Based on action, add the final part:
+		switch (SqlParser::normalizeKeyword($components['action'])) {
+			case 'ADD':
+				$query .= ' ' . $this->compileFieldCfg($components['definition']);
+				break;
+			case 'CHANGE':
+				$query .= ' ' . $components['newField'] . ' ' . $this->compileFieldCfg($components['definition']);
+				break;
+			case 'DROP':
+			case 'DROPKEY':
+				break;
+			case 'ADDKEY':
+			case 'ADDPRIMARYKEY':
+			case 'ADDUNIQUE':
+				$query .= ' (' . implode(',', $components['fields']) . ')';
+				break;
+			case 'DEFAULTCHARACTERSET':
+				$query .= $components['charset'];
+				break;
+			case 'ENGINE':
+				$query .= '= ' . $components['engine'];
+				break;
+		}
+		// Return query
+		return $query;
+	}
+
+	/**
+	 * Compiles a "SELECT [output] FROM..:" field list based on input array (made with ->parseFieldList())
+	 * Can also compile field lists for ORDER BY and GROUP BY.
+	 *
+	 * @param array $selectFields Array of select fields, (made with ->parseFieldList())
+	 * @param bool $compileComments Whether comments should be compiled
+	 * @param bool $functionMapping
+	 * @return string Select field string
+	 * @see parseFieldList()
+	 */
+	public function compileFieldList($selectFields, $compileComments = TRUE, $functionMapping = TRUE) {
+		// Prepare buffer variable:
+		$fields = '';
+		// Traverse the selectFields if any:
+		if (is_array($selectFields)) {
+			$outputParts = array();
+			foreach ($selectFields as $k => $v) {
+				// Detecting type:
+				switch ($v['type']) {
+					case 'function':
+						$outputParts[$k] = $v['function'] . '(' . $v['func_content'] . ')';
+						break;
+					case 'flow-control':
+						if ($v['flow-control']['type'] === 'CASE') {
+							$outputParts[$k] = $this->compileCaseStatement($v['flow-control']);
+						}
+						break;
+					case 'field':
+						$outputParts[$k] = ($v['distinct'] ? $v['distinct'] : '') . ($v['table'] ? $v['table'] . '.' : '') . $v['field'];
+						break;
+				}
+				// Alias:
+				if ($v['as']) {
+					$outputParts[$k] .= ' ' . $v['as_keyword'] . ' ' . $v['as'];
+				}
+				// Specifically for ORDER BY and GROUP BY field lists:
+				if ($v['sortDir']) {
+					$outputParts[$k] .= ' ' . $v['sortDir'];
+				}
+			}
+			if ($compileComments && $selectFields[0]['comments']) {
+				$fields = $selectFields[0]['comments'] . ' ';
+			}
+			$fields .= implode(', ', $outputParts);
+		}
+		return $fields;
+	}
+
+	/**
+	 * Add slashes function used for compiling queries
+	 * This method overrides the method from \TYPO3\CMS\Dbal\Database\NativeSqlParser because
+	 * the input string is already properly escaped.
+	 *
+	 * @param string $str Input string
+	 * @return string Output string
+	 */
+	protected function compileAddslashes($str) {
+		$search = array('\\', '\'', '"', "\x00", "\x0a", "\x0d", "\x1a");
+		$replace = array('\\\\', '\\\'', '\\"', '\0', '\n', '\r', '\Z');
+
+		return str_replace($search, $replace, $str);
+	}
+
+	/**
+	 * Compile field definition
+	 *
+	 * @param array $fieldCfg Field definition parts
+	 * @return string Field definition string
+	 */
+	public function compileFieldCfg($fieldCfg) {
+		// Set type:
+		$cfg = $fieldCfg['fieldType'];
+		// Add value, if any:
+		if ((string)$fieldCfg['value'] !== '') {
+			$cfg .= '(' . $fieldCfg['value'] . ')';
+		}
+		// Add additional features:
+		if (is_array($fieldCfg['featureIndex'])) {
+			foreach ($fieldCfg['featureIndex'] as $featureDef) {
+				$cfg .= ' ' . $featureDef['keyword'];
+				// Add value if found:
+				if (is_array($featureDef['value'])) {
+					$cfg .= ' ' . $featureDef['value'][1] . $this->compileAddslashes($featureDef['value'][0]) . $featureDef['value'][1];
+				}
+			}
+		}
+		// Return field definition string:
+		return $cfg;
+	}
+
+	/**
+	 * Implodes an array of WHERE clause configuration into a WHERE clause.
+	 *
+	 * @param array $clauseArray WHERE clause configuration
+	 * @param bool $functionMapping
+	 * @return string WHERE clause as string.
+	 * @see explodeWhereClause()
+	 */
+	public function compileWhereClause($clauseArray, $functionMapping = TRUE) {
+		// Prepare buffer variable:
+		$output = '';
+		// Traverse clause array:
+		if (is_array($clauseArray)) {
+			foreach ($clauseArray as $k => $v) {
+				// Set operator:
+				$output .= $v['operator'] ? ' ' . $v['operator'] : '';
+				// Look for sublevel:
+				if (is_array($v['sub'])) {
+					$output .= ' (' . trim($this->compileWhereClause($v['sub'])) . ')';
+				} elseif (isset($v['func']) && $v['func']['type'] === 'EXISTS') {
+					$output .= ' ' . trim($v['modifier']) . ' EXISTS (' . $this->compileSELECT($v['func']['subquery']) . ')';
+				} else {
+					if (isset($v['func']) && $v['func']['type'] === 'LOCATE') {
+						$output .= ' ' . trim($v['modifier']) . ' LOCATE(';
+						$output .= $v['func']['substr'][1] . $v['func']['substr'][0] . $v['func']['substr'][1];
+						$output .= ', ' . ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
+						$output .= isset($v['func']['pos']) ? ', ' . $v['func']['pos'][0] : '';
+						$output .= ')';
+					} elseif (isset($v['func']) && $v['func']['type'] === 'IFNULL') {
+						$output .= ' ' . trim($v['modifier']) . ' IFNULL(';
+						$output .= ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
+						$output .= ', ' . $v['func']['default'][1] . $this->compileAddslashes($v['func']['default'][0]) . $v['func']['default'][1];
+						$output .= ')';
+					} elseif (isset($v['func']) && $v['func']['type'] === 'CAST') {
+						$output .= ' ' . trim($v['modifier']) . ' CAST(';
+						$output .= ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
+						$output .= ' AS ' . $v['func']['datatype'][0];
+						$output .= ')';
+					} elseif (isset($v['func']) && $v['func']['type'] === 'FIND_IN_SET') {
+						$output .= ' ' . trim($v['modifier']) . ' FIND_IN_SET(';
+						$output .= $v['func']['str'][1] . $v['func']['str'][0] . $v['func']['str'][1];
+						$output .= ', ' . ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
+						$output .= ')';
+					} else {
+						// Set field/table with modifying prefix if any:
+						$output .= ' ' . trim(($v['modifier'] . ' ' . ($v['table'] ? $v['table'] . '.' : '') . $v['field']));
+						// Set calculation, if any:
+						if ($v['calc']) {
+							$output .= $v['calc'] . $v['calc_value'][1] . $this->compileAddslashes($v['calc_value'][0]) . $v['calc_value'][1];
+						}
+					}
+					// Set comparator:
+					if ($v['comparator']) {
+						$output .= ' ' . $v['comparator'];
+						// Detecting value type; list or plain:
+						if (GeneralUtility::inList('NOTIN,IN', SqlParser::normalizeKeyword($v['comparator']))) {
+							if (isset($v['subquery'])) {
+								$output .= ' (' . $this->compileSELECT($v['subquery']) . ')';
+							} else {
+								$valueBuffer = array();
+								foreach ($v['value'] as $realValue) {
+									$valueBuffer[] = $realValue[1] . $this->compileAddslashes($realValue[0]) . $realValue[1];
+								}
+								$output .= ' (' . trim(implode(',', $valueBuffer)) . ')';
+							}
+						} else {
+							if (GeneralUtility::inList('BETWEEN,NOT BETWEEN', $v['comparator'])) {
+								$lbound = $v['values'][0];
+								$ubound = $v['values'][1];
+								$output .= ' ' . $lbound[1] . $this->compileAddslashes($lbound[0]) . $lbound[1];
+								$output .= ' AND ';
+								$output .= $ubound[1] . $this->compileAddslashes($ubound[0]) . $ubound[1];
+							} else {
+								if (isset($v['value']['operator'])) {
+									$values = array();
+									foreach ($v['value']['args'] as $fieldDef) {
+										$values[] = ($fieldDef['table'] ? $fieldDef['table'] . '.' : '') . $fieldDef['field'];
+									}
+									$output .= ' ' . $v['value']['operator'] . '(' . implode(',', $values) . ')';
+								} else {
+									$output .= ' ' . $v['value'][1] . $this->compileAddslashes($v['value'][0]) . $v['value'][1];
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+		// Return output buffer:
+		return $output;
+	}
+
+}
diff --git a/typo3/sysext/dbal/Classes/Database/SqlParser.php b/typo3/sysext/dbal/Classes/Database/SqlParser.php
index 387708d6f0094a22a832103a44a1bb3e5df155f8..df47261168d15c815018e7648e2011f267859bbc 100644
--- a/typo3/sysext/dbal/Classes/Database/SqlParser.php
+++ b/typo3/sysext/dbal/Classes/Database/SqlParser.php
@@ -19,20 +19,77 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
 /**
  * PHP SQL engine / server
  */
-class SqlParser extends \TYPO3\CMS\Core\Database\SqlParser {
+class SqlParser {
+
+	/**
+	 * Parsing error string
+	 *
+	 * @var string
+	 */
+	public $parse_error = '';
+
+	/**
+	 * Last stop keyword used.
+	 *
+	 * @var string
+	 */
+	public $lastStopKeyWord = '';
+
+	/**
+	 * Find "comparator"
+	 *
+	 * @var array
+	 */
+	static protected $comparatorPatterns = array(
+		'<=',
+		'>=',
+		'<>',
+		'<',
+		'>',
+		'=',
+		'!=',
+		'NOT[[:space:]]+IN',
+		'IN',
+		'NOT[[:space:]]+LIKE[[:space:]]+BINARY',
+		'LIKE[[:space:]]+BINARY',
+		'NOT[[:space:]]+LIKE',
+		'LIKE',
+		'IS[[:space:]]+NOT',
+		'IS',
+		'BETWEEN',
+		'NOT[[:space]]+BETWEEN'
+	);
+
+	/**
+	 * Whitespaces in a query
+	 *
+	 * @var array
+	 */
+	static protected $interQueryWhitespaces = array(' ', TAB, CR, LF);
 
 	/**
 	 * @var DatabaseConnection
 	 */
 	protected $databaseConnection;
 
+	/**
+	 * @var SqlCompilers\Mysql
+	 */
+	protected $nativeSqlCompiler;
+
+	/**
+	 * @var SqlCompilers\Adodb
+	 */
+
+	protected $sqlCompiler;
+
 	/**
 	 * @param DatabaseConnection $databaseConnection
 	 */
 	public function __construct(DatabaseConnection $databaseConnection = NULL) {
-		parent::__construct();
-
 		$this->databaseConnection = $databaseConnection ?: $GLOBALS['TYPO3_DB'];
+		$this->sqlCompiler = GeneralUtility::makeInstance(SqlCompilers\Adodb::class);
+		$this->nativeSqlCompiler = GeneralUtility::makeInstance(SqlCompilers\Mysql::class);
 	}
 
 	/**
@@ -48,15 +105,39 @@ class SqlParser extends \TYPO3\CMS\Core\Database\SqlParser {
 				if ($this->databaseConnection->runningADOdbDriver('mssql')) {
 					$value = $this->getValueInQuotesMssql($parseString, $quote);
 				} else {
-					$value = parent::getValueInQuotes($parseString, $quote);
+					$value = $this->getValueInQuotesGeneric($parseString, $quote);
 				}
 				break;
 			default:
-				$value = parent::getValueInQuotes($parseString, $quote);
+				$value = $this->getValueInQuotesGeneric($parseString, $quote);
 		}
 		return $value;
 	}
 
+	/**
+	 * Get value in quotes from $parseString.
+	 * NOTICE: If a query being parsed was prepared for another database than MySQL this function should probably be changed
+	 *
+	 * @param string $parseString String from which to find value in quotes. Notice that $parseString is passed by reference and is shortend by the output of this function.
+	 * @param string $quote The quote used; input either " or '
+	 * @return string The value, passed through stripslashes() !
+	 */
+	protected function getValueInQuotesGeneric(&$parseString, $quote) {
+		$parts = explode($quote, substr($parseString, 1));
+		$buffer = '';
+		foreach ($parts as $k => $v) {
+			$buffer .= $v;
+			$reg = array();
+			preg_match('/\\\\$/', $v, $reg);
+			if ($reg && strlen($reg[0]) % 2) {
+				$buffer .= $quote;
+			} else {
+				$parseString = ltrim(substr($parseString, strlen($buffer) + 2));
+				return $this->parseStripslashes($buffer);
+			}
+		}
+	}
+
 	/**
 	 * Gets value in quotes from $parseString. This method targets MSSQL exclusively.
 	 *
@@ -107,373 +188,1275 @@ class SqlParser extends \TYPO3\CMS\Core\Database\SqlParser {
 		return '';
 	}
 
+
+	/*************************************
+	 *
+	 * SQL Parsing, full queries
+	 *
+	 **************************************/
 	/**
-	 * Compiles a "SELECT [output] FROM..:" field list based on input array (made with ->parseFieldList())
-	 * Can also compile field lists for ORDER BY and GROUP BY.
+	 * Parses any single SQL query
 	 *
-	 * @param array $selectFields Array of select fields, (made with ->parseFieldList())
-	 * @param bool $compileComments Whether comments should be compiled
-	 * @param bool $functionMapping Whether function mapping should take place
-	 * @return string Select field string
-	 * @see parseFieldList()
+	 * @param string $parseString SQL query
+	 * @return array Result array with all the parts in - or error message string
+	 * @see compileSQL(), debug_testSQL()
 	 */
-	public function compileFieldList($selectFields, $compileComments = TRUE, $functionMapping = TRUE) {
-		$output = '';
-		switch ((string)$this->databaseConnection->handlerCfg[$this->databaseConnection->lastHandlerKey]['type']) {
-			case 'native':
-				$output = parent::compileFieldList($selectFields, $compileComments);
+	public function parseSQL($parseString) {
+		// Prepare variables:
+		$parseString = $this->trimSQL($parseString);
+		$this->parse_error = '';
+		$result = array();
+		// Finding starting keyword of string:
+		$_parseString = $parseString;
+		// Protecting original string...
+		$keyword = $this->nextPart($_parseString, '^(SELECT|UPDATE|INSERT[[:space:]]+INTO|DELETE[[:space:]]+FROM|EXPLAIN|(DROP|CREATE|ALTER|TRUNCATE)[[:space:]]+TABLE|CREATE[[:space:]]+DATABASE)[[:space:]]+');
+		$keyword = $this->normalizeKeyword($keyword);
+		switch ($keyword) {
+			case 'SELECT':
+				// Parsing SELECT query:
+				$result = $this->parseSELECT($parseString);
 				break;
-			case 'adodb':
-				// Traverse the selectFields if any:
-				if (is_array($selectFields)) {
-					$outputParts = array();
-					foreach ($selectFields as $k => $v) {
-						// Detecting type:
-						switch ($v['type']) {
-							case 'function':
-								$outputParts[$k] = $v['function'] . '(' . $v['func_content'] . ')';
-								break;
-							case 'flow-control':
-								if ($v['flow-control']['type'] === 'CASE') {
-									$outputParts[$k] = $this->compileCaseStatement($v['flow-control'], $functionMapping);
-								}
-								break;
-							case 'field':
-								$outputParts[$k] = ($v['distinct'] ? $v['distinct'] : '') . ($v['table'] ? $v['table'] . '.' : '') . $v['field'];
-								break;
-						}
-						// Alias:
-						if ($v['as']) {
-							$outputParts[$k] .= ' ' . $v['as_keyword'] . ' ' . $v['as'];
+			case 'UPDATE':
+				// Parsing UPDATE query:
+				$result = $this->parseUPDATE($parseString);
+				break;
+			case 'INSERTINTO':
+				// Parsing INSERT query:
+				$result = $this->parseINSERT($parseString);
+				break;
+			case 'DELETEFROM':
+				// Parsing DELETE query:
+				$result = $this->parseDELETE($parseString);
+				break;
+			case 'EXPLAIN':
+				// Parsing EXPLAIN SELECT query:
+				$result = $this->parseEXPLAIN($parseString);
+				break;
+			case 'DROPTABLE':
+				// Parsing DROP TABLE query:
+				$result = $this->parseDROPTABLE($parseString);
+				break;
+			case 'ALTERTABLE':
+				// Parsing ALTER TABLE query:
+				$result = $this->parseALTERTABLE($parseString);
+				break;
+			case 'CREATETABLE':
+				// Parsing CREATE TABLE query:
+				$result = $this->parseCREATETABLE($parseString);
+				break;
+			case 'CREATEDATABASE':
+				// Parsing CREATE DATABASE query:
+				$result = $this->parseCREATEDATABASE($parseString);
+				break;
+			case 'TRUNCATETABLE':
+				// Parsing TRUNCATE TABLE query:
+				$result = $this->parseTRUNCATETABLE($parseString);
+				break;
+			default:
+				$result = $this->parseError('"' . $keyword . '" is not a keyword', $parseString);
+		}
+		return $result;
+	}
+
+	/**
+	 * Parsing SELECT query
+	 *
+	 * @param string $parseString SQL string with SELECT query to parse
+	 * @param array $parameterReferences Array holding references to either named (:name) or question mark (?) parameters found
+	 * @return mixed Returns array with components of SELECT query on success, otherwise an error message string.
+	 * @see compileSELECT()
+	 */
+	protected function parseSELECT($parseString, &$parameterReferences = NULL) {
+		// Removing SELECT:
+		$parseString = $this->trimSQL($parseString);
+		$parseString = ltrim(substr($parseString, 6));
+		// Init output variable:
+		$result = array();
+		if ($parameterReferences === NULL) {
+			$result['parameters'] = array();
+			$parameterReferences = &$result['parameters'];
+		}
+		$result['type'] = 'SELECT';
+		// Looking for STRAIGHT_JOIN keyword:
+		$result['STRAIGHT_JOIN'] = $this->nextPart($parseString, '^(STRAIGHT_JOIN)[[:space:]]+');
+		// Select fields:
+		$result['SELECT'] = $this->parseFieldList($parseString, '^(FROM)[[:space:]]+');
+		if ($this->parse_error) {
+			return $this->parse_error;
+		}
+		// Continue if string is not ended:
+		if ($parseString) {
+			// Get table list:
+			$result['FROM'] = $this->parseFromTables($parseString, '^(WHERE)[[:space:]]+');
+			if ($this->parse_error) {
+				return $this->parse_error;
+			}
+			// If there are more than just the tables (a WHERE clause that would be...)
+			if ($parseString) {
+				// Get WHERE clause:
+				$result['WHERE'] = $this->parseWhereClause($parseString, '^((GROUP|ORDER)[[:space:]]+BY|LIMIT)[[:space:]]+', $parameterReferences);
+				if ($this->parse_error) {
+					return $this->parse_error;
+				}
+				// If the WHERE clause parsing was stopped by GROUP BY, ORDER BY or LIMIT, then proceed with parsing:
+				if ($this->lastStopKeyWord) {
+					// GROUP BY parsing:
+					if ($this->lastStopKeyWord === 'GROUPBY') {
+						$result['GROUPBY'] = $this->parseFieldList($parseString, '^(ORDER[[:space:]]+BY|LIMIT)[[:space:]]+');
+						if ($this->parse_error) {
+							return $this->parse_error;
 						}
-						// Specifically for ORDER BY and GROUP BY field lists:
-						if ($v['sortDir']) {
-							$outputParts[$k] .= ' ' . $v['sortDir'];
+					}
+					// ORDER BY parsing:
+					if ($this->lastStopKeyWord === 'ORDERBY') {
+						$result['ORDERBY'] = $this->parseFieldList($parseString, '^(LIMIT)[[:space:]]+');
+						if ($this->parse_error) {
+							return $this->parse_error;
 						}
 					}
-					// @todo Handle SQL hints in comments according to current DBMS
-					if (FALSE && $selectFields[0]['comments']) {
-						$output = $selectFields[0]['comments'] . ' ';
+					// LIMIT parsing:
+					if ($this->lastStopKeyWord === 'LIMIT') {
+						if (preg_match('/^([0-9]+|[0-9]+[[:space:]]*,[[:space:]]*[0-9]+)$/', trim($parseString))) {
+							$result['LIMIT'] = $parseString;
+						} else {
+							return $this->parseError('No value for limit!', $parseString);
+						}
 					}
-					$output .= implode(', ', $outputParts);
 				}
-				break;
+			}
+		} else {
+			return $this->parseError('No table to select from!', $parseString);
 		}
-		return $output;
+		// Store current parseString in the result array for possible further processing (e.g., subquery support by DBAL)
+		$result['parseString'] = $parseString;
+		// Return result:
+		return $result;
 	}
 
 	/**
-	 * Compiles a CASE ... WHEN flow-control construct based on input array (made with ->parseCaseStatement())
+	 * Parsing UPDATE query
 	 *
-	 * @param array $components Array of case components, (made with ->parseCaseStatement())
-	 * @param bool $functionMapping Whether function mapping should take place
-	 * @return string case when string
-	 * @see parseCaseStatement()
+	 * @param string $parseString SQL string with UPDATE query to parse
+	 * @return mixed Returns array with components of UPDATE query on success, otherwise an error message string.
+	 * @see compileUPDATE()
 	 */
-	protected function compileCaseStatement(array $components, $functionMapping = TRUE) {
-		$output = '';
-		switch ((string)$this->databaseConnection->handlerCfg[$this->databaseConnection->lastHandlerKey]['type']) {
-			case 'native':
-				$output = parent::compileCaseStatement($components);
-				break;
-			case 'adodb':
-				$statement = 'CASE';
-				if (isset($components['case_field'])) {
-					$statement .= ' ' . $components['case_field'];
-				} elseif (isset($components['case_value'])) {
-					$statement .= ' ' . $components['case_value'][1] . $components['case_value'][0] . $components['case_value'][1];
-				}
-				foreach ($components['when'] as $when) {
-					$statement .= ' WHEN ';
-					$statement .= $this->compileWhereClause($when['when_value'], $functionMapping);
-					$statement .= ' THEN ';
-					$statement .= $when['then_value'][1] . $when['then_value'][0] . $when['then_value'][1];
-				}
-				if (isset($components['else'])) {
-					$statement .= ' ELSE ';
-					$statement .= $components['else'][1] . $components['else'][0] . $components['else'][1];
-				}
-				$statement .= ' END';
-				$output = $statement;
-				break;
+	protected function parseUPDATE($parseString) {
+		// Removing UPDATE
+		$parseString = $this->trimSQL($parseString);
+		$parseString = ltrim(substr($parseString, 6));
+		// Init output variable:
+		$result = array();
+		$result['type'] = 'UPDATE';
+		// Get table:
+		$result['TABLE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+');
+		// Continue if string is not ended:
+		if ($result['TABLE']) {
+			if ($parseString && $this->nextPart($parseString, '^(SET)[[:space:]]+')) {
+				$comma = TRUE;
+				// Get field/value pairs:
+				while ($comma) {
+					if ($fieldName = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]*=')) {
+						// Strip off "=" sign.
+						$this->nextPart($parseString, '^(=)');
+						$value = $this->getValue($parseString);
+						$result['FIELDS'][$fieldName] = $value;
+					} else {
+						return $this->parseError('No fieldname found', $parseString);
+					}
+					$comma = $this->nextPart($parseString, '^(,)');
+				}
+				// WHERE
+				if ($this->nextPart($parseString, '^(WHERE)')) {
+					$result['WHERE'] = $this->parseWhereClause($parseString);
+					if ($this->parse_error) {
+						return $this->parse_error;
+					}
+				}
+			} else {
+				return $this->parseError('Query missing SET...', $parseString);
+			}
+		} else {
+			return $this->parseError('No table found!', $parseString);
+		}
+		// Should be no more content now:
+		if ($parseString) {
+			return $this->parseError('Still content in clause after parsing!', $parseString);
 		}
-		return $output;
+		// Return result:
+		return $result;
 	}
 
 	/**
-	 * Add slashes function used for compiling queries
-	 * This method overrides the method from \TYPO3\CMS\Core\Database\SqlParser because
-	 * the input string is already properly escaped.
+	 * Parsing INSERT query
 	 *
-	 * @param string $str Input string
-	 * @return string Output string
+	 * @param string $parseString SQL string with INSERT query to parse
+	 * @return mixed Returns array with components of INSERT query on success, otherwise an error message string.
+	 * @see compileINSERT()
 	 */
-	protected function compileAddslashes($str) {
-		return $str;
+	protected function parseINSERT($parseString) {
+		// Removing INSERT
+		$parseString = $this->trimSQL($parseString);
+		$parseString = ltrim(substr(ltrim(substr($parseString, 6)), 4));
+		// Init output variable:
+		$result = array();
+		$result['type'] = 'INSERT';
+		// Get table:
+		$result['TABLE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)([[:space:]]+|\\()');
+		if ($result['TABLE']) {
+			// In this case there are no field names mentioned in the SQL!
+			if ($this->nextPart($parseString, '^(VALUES)([[:space:]]+|\\()')) {
+				// Get values/fieldnames (depending...)
+				$result['VALUES_ONLY'] = $this->getValue($parseString, 'IN');
+				if ($this->parse_error) {
+					return $this->parse_error;
+				}
+				if (preg_match('/^,/', $parseString)) {
+					$result['VALUES_ONLY'] = array($result['VALUES_ONLY']);
+					$result['EXTENDED'] = '1';
+					while ($this->nextPart($parseString, '^(,)') === ',') {
+						$result['VALUES_ONLY'][] = $this->getValue($parseString, 'IN');
+						if ($this->parse_error) {
+							return $this->parse_error;
+						}
+					}
+				}
+			} else {
+				// There are apparently fieldnames listed:
+				$fieldNames = $this->getValue($parseString, '_LIST');
+				if ($this->parse_error) {
+					return $this->parse_error;
+				}
+				// "VALUES" keyword binds the fieldnames to values:
+				if ($this->nextPart($parseString, '^(VALUES)([[:space:]]+|\\()')) {
+					$result['FIELDS'] = array();
+					do {
+						// Using the "getValue" function to get the field list...
+						$values = $this->getValue($parseString, 'IN');
+						if ($this->parse_error) {
+							return $this->parse_error;
+						}
+						$insertValues = array();
+						foreach ($fieldNames as $k => $fN) {
+							if (preg_match('/^[[:alnum:]_]+$/', $fN)) {
+								if (isset($values[$k])) {
+									if (!isset($insertValues[$fN])) {
+										$insertValues[$fN] = $values[$k];
+									} else {
+										return $this->parseError('Fieldname ("' . $fN . '") already found in list!', $parseString);
+									}
+								} else {
+									return $this->parseError('No value set!', $parseString);
+								}
+							} else {
+								return $this->parseError('Invalid fieldname ("' . $fN . '")', $parseString);
+							}
+						}
+						if (isset($values[$k + 1])) {
+							return $this->parseError('Too many values in list!', $parseString);
+						}
+						$result['FIELDS'][] = $insertValues;
+					} while ($this->nextPart($parseString, '^(,)') === ',');
+					if (count($result['FIELDS']) === 1) {
+						$result['FIELDS'] = $result['FIELDS'][0];
+					} else {
+						$result['EXTENDED'] = '1';
+					}
+				} else {
+					return $this->parseError('VALUES keyword expected', $parseString);
+				}
+			}
+		} else {
+			return $this->parseError('No table found!', $parseString);
+		}
+		// Should be no more content now:
+		if ($parseString) {
+			return $this->parseError('Still content after parsing!', $parseString);
+		}
+		// Return result
+		return $result;
 	}
 
-	/*************************
+	/**
+	 * Parsing DELETE query
 	 *
-	 * Compiling queries
+	 * @param string $parseString SQL string with DELETE query to parse
+	 * @return mixed Returns array with components of DELETE query on success, otherwise an error message string.
+	 * @see compileDELETE()
+	 */
+	protected function parseDELETE($parseString) {
+		// Removing DELETE
+		$parseString = $this->trimSQL($parseString);
+		$parseString = ltrim(substr(ltrim(substr($parseString, 6)), 4));
+		// Init output variable:
+		$result = array();
+		$result['type'] = 'DELETE';
+		// Get table:
+		$result['TABLE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+');
+		if ($result['TABLE']) {
+			// WHERE
+			if ($this->nextPart($parseString, '^(WHERE)')) {
+				$result['WHERE'] = $this->parseWhereClause($parseString);
+				if ($this->parse_error) {
+					return $this->parse_error;
+				}
+			}
+		} else {
+			return $this->parseError('No table found!', $parseString);
+		}
+		// Should be no more content now:
+		if ($parseString) {
+			return $this->parseError('Still content in clause after parsing!', $parseString);
+		}
+		// Return result:
+		return $result;
+	}
+
+	/**
+	 * Parsing EXPLAIN query
 	 *
-	 *************************/
+	 * @param string $parseString SQL string with EXPLAIN query to parse
+	 * @return mixed Returns array with components of EXPLAIN query on success, otherwise an error message string.
+	 * @see parseSELECT()
+	 */
+	protected function parseEXPLAIN($parseString) {
+		// Removing EXPLAIN
+		$parseString = $this->trimSQL($parseString);
+		$parseString = ltrim(substr($parseString, 6));
+		// Init output variable:
+		$result = $this->parseSELECT($parseString);
+		if (is_array($result)) {
+			$result['type'] = 'EXPLAIN';
+		}
+		return $result;
+	}
+
 	/**
-	 * Compiles an INSERT statement from components array
+	 * Parsing CREATE TABLE query
 	 *
-	 * @param array Array of SQL query components
-	 * @return string SQL INSERT query / array
-	 * @see parseINSERT()
+	 * @param string $parseString SQL string starting with CREATE TABLE
+	 * @return mixed Returns array with components of CREATE TABLE query on success, otherwise an error message string.
+	 * @see compileCREATETABLE()
 	 */
-	protected function compileINSERT($components) {
-		$query = '';
-		switch ((string)$this->databaseConnection->handlerCfg[$this->databaseConnection->lastHandlerKey]['type']) {
-			case 'native':
-				$query = parent::compileINSERT($components);
-				break;
-			case 'adodb':
-				$values = array();
-				if (isset($components['VALUES_ONLY']) && is_array($components['VALUES_ONLY'])) {
-					$valuesComponents = $components['EXTENDED'] === '1' ? $components['VALUES_ONLY'] : array($components['VALUES_ONLY']);
-					$tableFields = array_keys($this->databaseConnection->cache_fieldType[$components['TABLE']]);
-				} else {
-					$valuesComponents = $components['EXTENDED'] === '1' ? $components['FIELDS'] : array($components['FIELDS']);
-					$tableFields = array_keys($valuesComponents[0]);
-				}
-				foreach ($valuesComponents as $valuesComponent) {
-					$fields = array();
-					$fc = 0;
-					foreach ($valuesComponent as $fV) {
-						$fields[$tableFields[$fc++]] = $fV[0];
+	protected function parseCREATETABLE($parseString) {
+		// Removing CREATE TABLE
+		$parseString = $this->trimSQL($parseString);
+		$parseString = ltrim(substr(ltrim(substr($parseString, 6)), 5));
+		// Init output variable:
+		$result = array();
+		$result['type'] = 'CREATETABLE';
+		// Get table:
+		$result['TABLE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]*\\(', TRUE);
+		if ($result['TABLE']) {
+			// While the parseString is not yet empty:
+			while ($parseString !== '') {
+				// Getting key
+				if ($key = $this->nextPart($parseString, '^(KEY|PRIMARY KEY|UNIQUE KEY|UNIQUE)([[:space:]]+|\\()')) {
+					$key = $this->normalizeKeyword($key);
+					switch ($key) {
+						case 'PRIMARYKEY':
+							$result['KEYS']['PRIMARYKEY'] = $this->getValue($parseString, '_LIST');
+							if ($this->parse_error) {
+								return $this->parse_error;
+							}
+							break;
+						case 'UNIQUE':
+
+						case 'UNIQUEKEY':
+							if ($keyName = $this->nextPart($parseString, '^([[:alnum:]_]+)([[:space:]]+|\\()')) {
+								$result['KEYS']['UNIQUE'] = array($keyName => $this->getValue($parseString, '_LIST'));
+								if ($this->parse_error) {
+									return $this->parse_error;
+								}
+							} else {
+								return $this->parseError('No keyname found', $parseString);
+							}
+							break;
+						case 'KEY':
+							if ($keyName = $this->nextPart($parseString, '^([[:alnum:]_]+)([[:space:]]+|\\()')) {
+								$result['KEYS'][$keyName] = $this->getValue($parseString, '_LIST', 'INDEX');
+								if ($this->parse_error) {
+									return $this->parse_error;
+								}
+							} else {
+								return $this->parseError('No keyname found', $parseString);
+							}
+							break;
+					}
+				} elseif ($fieldName = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+')) {
+					// Getting field:
+					$result['FIELDS'][$fieldName]['definition'] = $this->parseFieldDef($parseString);
+					if ($this->parse_error) {
+						return $this->parse_error;
 					}
-					$values[] = $fields;
 				}
-				$query = count($values) === 1 ? $values[0] : $values;
-				break;
+				// Finding delimiter:
+				$delim = $this->nextPart($parseString, '^(,|\\))');
+				if (!$delim) {
+					return $this->parseError('No delimiter found', $parseString);
+				} elseif ($delim === ')') {
+					break;
+				}
+			}
+			// Finding what is after the table definition - table type in MySQL
+			if ($delim === ')') {
+				if ($this->nextPart($parseString, '^((ENGINE|TYPE)[[:space:]]*=)')) {
+					$result['engine'] = $parseString;
+					$parseString = '';
+				}
+			} else {
+				return $this->parseError('No fieldname found!', $parseString);
+			}
+		} else {
+			return $this->parseError('No table found!', $parseString);
+		}
+		// Should be no more content now:
+		if ($parseString) {
+			return $this->parseError('Still content in clause after parsing!', $parseString);
 		}
-		return $query;
+		return $result;
 	}
 
 	/**
-	 * Compiles a CREATE TABLE statement from components array
+	 * Parsing ALTER TABLE query
 	 *
-	 * @param array $components Array of SQL query components
-	 * @return array array with SQL CREATE TABLE/INDEX command(s)
-	 * @see parseCREATETABLE()
-	 */
-	public function compileCREATETABLE($components) {
-		$query = array();
-		// Execute query (based on handler derived from the TABLE name which we actually know for once!)
-		switch ((string)$this->databaseConnection->handlerCfg[$this->databaseConnection->handler_getFromTableList($components['TABLE'])]['type']) {
-			case 'native':
-				$query[] = parent::compileCREATETABLE($components);
-				break;
-			case 'adodb':
-				// Create fields and keys:
-				$fieldsKeys = array();
-				$indexKeys = array();
-				foreach ($components['FIELDS'] as $fN => $fCfg) {
-					$handlerKey = $this->databaseConnection->handler_getFromTableList($components['TABLE']);
-					$fieldsKeys[$fN] = $this->databaseConnection->quoteName($fN, $handlerKey, TRUE) . ' ' . $this->compileFieldCfg($fCfg['definition']);
-				}
-				if (isset($components['KEYS']) && is_array($components['KEYS'])) {
-					foreach ($components['KEYS'] as $kN => $kCfg) {
-						if ($kN === 'PRIMARYKEY') {
-							foreach ($kCfg as $field) {
-								$fieldsKeys[$field] .= ' PRIMARY';
+	 * @param string $parseString SQL string starting with ALTER TABLE
+	 * @return mixed Returns array with components of ALTER TABLE query on success, otherwise an error message string.
+	 * @see compileALTERTABLE()
+	 */
+	protected function parseALTERTABLE($parseString) {
+		// Removing ALTER TABLE
+		$parseString = $this->trimSQL($parseString);
+		$parseString = ltrim(substr(ltrim(substr($parseString, 5)), 5));
+		// Init output variable:
+		$result = array();
+		$result['type'] = 'ALTERTABLE';
+		// Get table:
+		$hasBackquote = $this->nextPart($parseString, '^(`)') === '`';
+		$result['TABLE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)' . ($hasBackquote ? '`' : '') . '[[:space:]]+');
+		if ($hasBackquote && $this->nextPart($parseString, '^(`)') !== '`') {
+			return $this->parseError('No end backquote found!', $parseString);
+		}
+		if ($result['TABLE']) {
+			if ($result['action'] = $this->nextPart($parseString, '^(CHANGE|DROP[[:space:]]+KEY|DROP[[:space:]]+PRIMARY[[:space:]]+KEY|ADD[[:space:]]+KEY|ADD[[:space:]]+PRIMARY[[:space:]]+KEY|ADD[[:space:]]+UNIQUE|DROP|ADD|RENAME|DEFAULT[[:space:]]+CHARACTER[[:space:]]+SET|ENGINE)([[:space:]]+|\\(|=)')) {
+				$actionKey = $this->normalizeKeyword($result['action']);
+				// Getting field:
+				if (GeneralUtility::inList('ADDPRIMARYKEY,DROPPRIMARYKEY,ENGINE', $actionKey) || ($fieldKey = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+'))) {
+					switch ($actionKey) {
+						case 'ADD':
+							$result['FIELD'] = $fieldKey;
+							$result['definition'] = $this->parseFieldDef($parseString);
+							if ($this->parse_error) {
+								return $this->parse_error;
+							}
+							break;
+						case 'DROP':
+						case 'RENAME':
+							$result['FIELD'] = $fieldKey;
+							break;
+						case 'CHANGE':
+							$result['FIELD'] = $fieldKey;
+							if ($result['newField'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+')) {
+								$result['definition'] = $this->parseFieldDef($parseString);
+								if ($this->parse_error) {
+									return $this->parse_error;
+								}
+							} else {
+								return $this->parseError('No NEW field name found', $parseString);
 							}
-						} elseif ($kN === 'UNIQUE') {
-							foreach ($kCfg as $n => $field) {
-								$indexKeys = array_merge($indexKeys, $this->compileCREATEINDEX($n, $components['TABLE'], $field, array('UNIQUE')));
+							break;
+						case 'ADDKEY':
+						case 'ADDPRIMARYKEY':
+						case 'ADDUNIQUE':
+							$result['KEY'] = $fieldKey;
+							$result['fields'] = $this->getValue($parseString, '_LIST', 'INDEX');
+							if ($this->parse_error) {
+								return $this->parse_error;
 							}
-						} else {
-							$indexKeys = array_merge($indexKeys, $this->compileCREATEINDEX($kN, $components['TABLE'], $kCfg));
-						}
+							break;
+						case 'DROPKEY':
+							$result['KEY'] = $fieldKey;
+							break;
+						case 'DROPPRIMARYKEY':
+							// @todo ???
+							break;
+						case 'DEFAULTCHARACTERSET':
+							$result['charset'] = $fieldKey;
+							break;
+						case 'ENGINE':
+							$result['engine'] = $this->nextPart($parseString, '^=[[:space:]]*([[:alnum:]]+)[[:space:]]+', TRUE);
+							break;
 					}
+				} else {
+					return $this->parseError('No field name found', $parseString);
 				}
-				// Generally create without OID on PostgreSQL
-				$tableOptions = array('postgres' => 'WITHOUT OIDS');
-				// Fetch table/index generation query:
-				$tableName = $this->databaseConnection->quoteName($components['TABLE'], NULL, TRUE);
-				$query = array_merge($this->databaseConnection->handlerInstance[$this->databaseConnection->lastHandlerKey]->DataDictionary->CreateTableSQL($tableName, implode(',' . LF, $fieldsKeys), $tableOptions), $indexKeys);
-				break;
+			} else {
+				return $this->parseError('No action CHANGE, DROP or ADD found!', $parseString);
+			}
+		} else {
+			return $this->parseError('No table found!', $parseString);
 		}
-		return $query;
+		// Should be no more content now:
+		if ($parseString) {
+			return $this->parseError('Still content in clause after parsing!', $parseString);
+		}
+		return $result;
 	}
 
 	/**
-	 * Compiles an ALTER TABLE statement from components array
+	 * Parsing DROP TABLE query
 	 *
-	 * @param array Array of SQL query components
-	 * @return string SQL ALTER TABLE query
-	 * @see parseALTERTABLE()
+	 * @param string $parseString SQL string starting with DROP TABLE
+	 * @return mixed Returns array with components of DROP TABLE query on success, otherwise an error message string.
 	 */
-	public function compileALTERTABLE($components) {
-		$query = '';
-		// Execute query (based on handler derived from the TABLE name which we actually know for once!)
-		switch ((string)$this->databaseConnection->handlerCfg[$this->databaseConnection->lastHandlerKey]['type']) {
-			case 'native':
-				$query = parent::compileALTERTABLE($components);
-				break;
-			case 'adodb':
-				$tableName = $this->databaseConnection->quoteName($components['TABLE'], NULL, TRUE);
-				$fieldName = $this->databaseConnection->quoteName($components['FIELD'], NULL, TRUE);
-				switch (strtoupper(str_replace(array(' ', "\n", "\r", "\t"), '', $components['action']))) {
-					case 'ADD':
-						$query = $this->databaseConnection->handlerInstance[$this->databaseConnection->lastHandlerKey]->DataDictionary->AddColumnSQL($tableName, $fieldName . ' ' . $this->compileFieldCfg($components['definition']));
-						break;
-					case 'CHANGE':
-						$query = $this->databaseConnection->handlerInstance[$this->databaseConnection->lastHandlerKey]->DataDictionary->AlterColumnSQL($tableName, $fieldName . ' ' . $this->compileFieldCfg($components['definition']));
-						break;
-					case 'DROP':
+	protected function parseDROPTABLE($parseString) {
+		// Removing DROP TABLE
+		$parseString = $this->trimSQL($parseString);
+		$parseString = ltrim(substr(ltrim(substr($parseString, 4)), 5));
+		// Init output variable:
+		$result = array();
+		$result['type'] = 'DROPTABLE';
+		// IF EXISTS
+		$result['ifExists'] = $this->nextPart($parseString, '^(IF[[:space:]]+EXISTS[[:space:]]+)');
+		// Get table:
+		$result['TABLE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+');
+		if ($result['TABLE']) {
+			// Should be no more content now:
+			if ($parseString) {
+				return $this->parseError('Still content in clause after parsing!', $parseString);
+			}
+			return $result;
+		} else {
+			return $this->parseError('No table found!', $parseString);
+		}
+	}
 
-					case 'DROPKEY':
-						$query = $this->compileDROPINDEX($components['KEY'], $components['TABLE']);
-						break;
+	/**
+	 * Parsing CREATE DATABASE query
+	 *
+	 * @param string $parseString SQL string starting with CREATE DATABASE
+	 * @return mixed Returns array with components of CREATE DATABASE query on success, otherwise an error message string.
+	 */
+	protected function parseCREATEDATABASE($parseString) {
+		// Removing CREATE DATABASE
+		$parseString = $this->trimSQL($parseString);
+		$parseString = ltrim(substr(ltrim(substr($parseString, 6)), 8));
+		// Init output variable:
+		$result = array();
+		$result['type'] = 'CREATEDATABASE';
+		// Get table:
+		$result['DATABASE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+');
+		if ($result['DATABASE']) {
+			// Should be no more content now:
+			if ($parseString) {
+				return $this->parseError('Still content in clause after parsing!', $parseString);
+			}
+			return $result;
+		} else {
+			return $this->parseError('No database found!', $parseString);
+		}
+	}
 
-					case 'ADDKEY':
-						$query = $this->compileCREATEINDEX($components['KEY'], $components['TABLE'], $components['fields']);
-						break;
-					case 'ADDUNIQUE':
-						$query = $this->compileCREATEINDEX($components['KEY'], $components['TABLE'], $components['fields'], array('UNIQUE'));
-						break;
-					case 'ADDPRIMARYKEY':
-						// @todo ???
-						break;
-					case 'DEFAULTCHARACTERSET':
+	/**
+	 * Parsing TRUNCATE TABLE query
+	 *
+	 * @param string $parseString SQL string starting with TRUNCATE TABLE
+	 * @return mixed Returns array with components of TRUNCATE TABLE query on success, otherwise an error message string.
+	 */
+	protected function parseTRUNCATETABLE($parseString) {
+		// Removing TRUNCATE TABLE
+		$parseString = $this->trimSQL($parseString);
+		$parseString = ltrim(substr(ltrim(substr($parseString, 8)), 5));
+		// Init output variable:
+		$result = array();
+		$result['type'] = 'TRUNCATETABLE';
+		// Get table:
+		$result['TABLE'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+');
+		if ($result['TABLE']) {
+			// Should be no more content now:
+			if ($parseString) {
+				return $this->parseError('Still content in clause after parsing!', $parseString);
+			}
+			return $result;
+		} else {
+			return $this->parseError('No table found!', $parseString);
+		}
+	}
 
-					case 'ENGINE':
-						// @todo ???
+	/**************************************
+	 *
+	 * SQL Parsing, helper functions for parts of queries
+	 *
+	 **************************************/
+	/**
+	 * Parsing the fields in the "SELECT [$selectFields] FROM" part of a query into an array.
+	 * The output from this function can be compiled back into a field list with ->compileFieldList()
+	 * Will detect the keywords "DESC" and "ASC" after the table name; thus is can be used for parsing the more simply ORDER BY and GROUP BY field lists as well!
+	 *
+	 * @param string $parseString The string with fieldnames, eg. "title, uid AS myUid, max(tstamp), count(*)" etc. NOTICE: passed by reference!
+	 * @param string $stopRegex Regular expressing to STOP parsing, eg. '^(FROM)([[:space:]]*)'
+	 * @return array If successful parsing, returns an array, otherwise an error string.
+	 * @see compileFieldList()
+	 */
+	public function parseFieldList(&$parseString, $stopRegex = '') {
+		$stack = array();
+		// Contains the parsed content
+		if ($parseString === '') {
+			return $stack;
+		}
+		// @todo - should never happen, why does it?
+		// Pointer to positions in $stack
+		$pnt = 0;
+		// Indicates the parenthesis level we are at.
+		$level = 0;
+		// Recursivity brake.
+		$loopExit = 0;
+		// Prepare variables:
+		$parseString = $this->trimSQL($parseString);
+		$this->lastStopKeyWord = '';
+		$this->parse_error = '';
+		// Parse any SQL hint / comments
+		$stack[$pnt]['comments'] = $this->nextPart($parseString, '^(\\/\\*.*\\*\\/)');
+		// $parseString is continuously shortened by the process and we keep parsing it till it is zero:
+		while ($parseString !== '') {
+			// Checking if we are inside / outside parenthesis (in case of a function like count(), max(), min() etc...):
+			// Inside parenthesis here (does NOT detect if values in quotes are used, the only token is ")" or "("):
+			if ($level > 0) {
+				// Accumulate function content until next () parenthesis:
+				$funcContent = $this->nextPart($parseString, '^([^()]*.)');
+				$stack[$pnt]['func_content.'][] = array(
+					'level' => $level,
+					'func_content' => substr($funcContent, 0, -1)
+				);
+				$stack[$pnt]['func_content'] .= $funcContent;
+				// Detecting ( or )
+				switch (substr($stack[$pnt]['func_content'], -1)) {
+					case '(':
+						$level++;
+						break;
+					case ')':
+						$level--;
+						// If this was the last parenthesis:
+						if (!$level) {
+							$stack[$pnt]['func_content'] = substr($stack[$pnt]['func_content'], 0, -1);
+							// Remove any whitespace after the parenthesis.
+							$parseString = ltrim($parseString);
+						}
 						break;
 				}
-				break;
+			} else {
+				// Outside parenthesis, looking for next field:
+				// Looking for a flow-control construct (only known constructs supported)
+				if (preg_match('/^case([[:space:]][[:alnum:]\\*._]+)?[[:space:]]when/i', $parseString)) {
+					$stack[$pnt]['type'] = 'flow-control';
+					$stack[$pnt]['flow-control'] = $this->parseCaseStatement($parseString);
+					// Looking for "AS" alias:
+					if ($as = $this->nextPart($parseString, '^(AS)[[:space:]]+')) {
+						$stack[$pnt]['as'] = $this->nextPart($parseString, '^([[:alnum:]_]+)(,|[[:space:]]+)');
+						$stack[$pnt]['as_keyword'] = $as;
+					}
+				} else {
+					// Looking for a known function (only known functions supported)
+					$func = $this->nextPart($parseString, '^(count|max|min|floor|sum|avg)[[:space:]]*\\(');
+					if ($func) {
+						// Strip off "("
+						$parseString = trim(substr($parseString, 1));
+						$stack[$pnt]['type'] = 'function';
+						$stack[$pnt]['function'] = $func;
+						// increse parenthesis level counter.
+						$level++;
+					} else {
+						$stack[$pnt]['distinct'] = $this->nextPart($parseString, '^(distinct[[:space:]]+)');
+						// Otherwise, look for regular fieldname:
+						if (($fieldName = $this->nextPart($parseString, '^([[:alnum:]\\*._]+)(,|[[:space:]]+)')) !== '') {
+							$stack[$pnt]['type'] = 'field';
+							// Explode fieldname into field and table:
+							$tableField = explode('.', $fieldName, 2);
+							if (count($tableField) === 2) {
+								$stack[$pnt]['table'] = $tableField[0];
+								$stack[$pnt]['field'] = $tableField[1];
+							} else {
+								$stack[$pnt]['table'] = '';
+								$stack[$pnt]['field'] = $tableField[0];
+							}
+						} else {
+							return $this->parseError('No field name found as expected in parseFieldList()', $parseString);
+						}
+					}
+				}
+			}
+			// After a function or field we look for "AS" alias and a comma to separate to the next field in the list:
+			if (!$level) {
+				// Looking for "AS" alias:
+				if ($as = $this->nextPart($parseString, '^(AS)[[:space:]]+')) {
+					$stack[$pnt]['as'] = $this->nextPart($parseString, '^([[:alnum:]_]+)(,|[[:space:]]+)');
+					$stack[$pnt]['as_keyword'] = $as;
+				}
+				// Looking for "ASC" or "DESC" keywords (for ORDER BY)
+				if ($sDir = $this->nextPart($parseString, '^(ASC|DESC)([[:space:]]+|,)')) {
+					$stack[$pnt]['sortDir'] = $sDir;
+				}
+				// Looking for stop-keywords:
+				if ($stopRegex && ($this->lastStopKeyWord = $this->nextPart($parseString, $stopRegex))) {
+					$this->lastStopKeyWord = $this->normalizeKeyword($this->lastStopKeyWord);
+					return $stack;
+				}
+				// Looking for comma (since the stop-keyword did not trigger a return...)
+				if ($parseString !== '' && !$this->nextPart($parseString, '^(,)')) {
+					return $this->parseError('No comma found as expected in parseFieldList()', $parseString);
+				}
+				// Increasing pointer:
+				$pnt++;
+			}
+			// Check recursivity brake:
+			$loopExit++;
+			if ($loopExit > 500) {
+				return $this->parseError('More than 500 loops, exiting prematurely in parseFieldList()...', $parseString);
+			}
 		}
-		return $query;
+		// Return result array:
+		return $stack;
 	}
 
 	/**
-	 * Compiles CREATE INDEX statements from component information
-	 *
-	 * MySQL only needs uniqueness of index names per table, but many DBMS require uniqueness of index names per schema.
-	 * The table name is hashed and prepended to the index name to make sure index names are unique.
+	 * Parsing a CASE ... WHEN flow-control construct.
+	 * The output from this function can be compiled back with ->compileCaseStatement()
 	 *
-	 * @param string $indexName
-	 * @param string $tableName
-	 * @param array $indexFields
-	 * @param array $indexOptions
-	 * @return array
-	 * @see compileALTERTABLE()
+	 * @param string $parseString The string with the CASE ... WHEN construct, eg. "CASE field WHEN 1 THEN 0 ELSE ..." etc. NOTICE: passed by reference!
+	 * @return array If successful parsing, returns an array, otherwise an error string.
+	 * @see compileCaseConstruct()
 	 */
-	public function compileCREATEINDEX($indexName, $tableName, $indexFields, $indexOptions = array()) {
-		$indexIdentifier = $this->databaseConnection->quoteName(hash('crc32b', $tableName) . '_' . $indexName, NULL, TRUE);
-		$dbmsSpecifics = $this->databaseConnection->getSpecifics();
-		$keepFieldLengths = $dbmsSpecifics->specificExists(Specifics\AbstractSpecifics::PARTIAL_STRING_INDEX) && $dbmsSpecifics->getSpecific(Specifics\AbstractSpecifics::PARTIAL_STRING_INDEX);
-
-		foreach ($indexFields as $key => $fieldName) {
-			if (!$keepFieldLengths) {
-				$fieldName = preg_replace('/\A([^\(]+)(\(\d+\))/', '\\1', $fieldName);
+	protected function parseCaseStatement(&$parseString) {
+		$result = array();
+		$result['type'] = $this->nextPart($parseString, '^(case)[[:space:]]+');
+		if (!preg_match('/^when[[:space:]]+/i', $parseString)) {
+			$value = $this->getValue($parseString);
+			if (!(isset($value[1]) || is_numeric($value[0]))) {
+				$result['case_field'] = $value[0];
+			} else {
+				$result['case_value'] = $value;
 			}
-			// Quote the fieldName in backticks with escaping, ADOdb will replace the backticks with the correct quoting
-			$indexFields[$key] = '`' . str_replace('`', '``', $fieldName) . '`';
 		}
-
-		return $this->databaseConnection->handlerInstance[$this->databaseConnection->handler_getFromTableList($tableName)]->DataDictionary->CreateIndexSQL(
-			$indexIdentifier, $this->databaseConnection->quoteName($tableName, NULL, TRUE), $indexFields, $indexOptions
-		);
+		$result['when'] = array();
+		while ($this->nextPart($parseString, '^(when)[[:space:]]')) {
+			$when = array();
+			$when['when_value'] = $this->parseWhereClause($parseString, '^(then)[[:space:]]+');
+			$when['then_value'] = $this->getValue($parseString);
+			$result['when'][] = $when;
+		}
+		if ($this->nextPart($parseString, '^(else)[[:space:]]+')) {
+			$result['else'] = $this->getValue($parseString);
+		}
+		if (!$this->nextPart($parseString, '^(end)[[:space:]]+')) {
+			return $this->parseError('No "end" keyword found as expected in parseCaseStatement()', $parseString);
+		}
+		return $result;
 	}
 
 	/**
-	 * Compiles DROP INDEX statements from component information
-	 *
-	 * MySQL only needs uniqueness of index names per table, but many DBMS require uniqueness of index names per schema.
-	 * The table name is hashed and prepended to the index name to make sure index names are unique.
+	 * Parsing a CAST definition in the "JOIN [$parseString] ..." part of a query into an array.
+	 * The success of this parsing determines if that part of the query is supported by TYPO3.
 	 *
-	 * @param $indexName
-	 * @param $tableName
-	 * @return array
-	 * @see compileALTERTABLE()
+	 * @param string $parseString JOIN clause to parse. NOTICE: passed by reference!
+	 * @return mixed If successful parsing, returns an array, otherwise an error string.
 	 */
-	public function compileDROPINDEX($indexName, $tableName) {
-		$indexIdentifier = $this->databaseConnection->quoteName(hash('crc32b', $tableName) . '_' . $indexName, NULL, TRUE);
-
-		return $this->databaseConnection->handlerInstance[$this->databaseConnection->handler_getFromTableList($tableName)]->DataDictionary->DropIndexSQL(
-			$indexIdentifier, $this->databaseConnection->quoteName($tableName)
-		);
+	protected function parseCastStatement(&$parseString) {
+		$this->nextPart($parseString, '^(CAST)[[:space:]]*');
+		$parseString = trim(substr($parseString, 1));
+		$castDefinition = array('type' => 'cast');
+		// Strip off "("
+		if ($fieldName = $this->nextPart($parseString, '^([[:alnum:]\\*._]+)[[:space:]]*')) {
+			// Parse field name into field and table:
+			$tableField = explode('.', $fieldName, 2);
+			if (count($tableField) === 2) {
+				$castDefinition['table'] = $tableField[0];
+				$castDefinition['field'] = $tableField[1];
+			} else {
+				$castDefinition['table'] = '';
+				$castDefinition['field'] = $tableField[0];
+			}
+		} else {
+			return $this->parseError('No casted join field found in parseCastStatement()!', $parseString);
+		}
+		if ($this->nextPart($parseString, '^([[:space:]]*AS[[:space:]]*)')) {
+			$castDefinition['datatype'] = $this->getValue($parseString);
+		}
+		if (!$this->nextPart($parseString, '^([)])')) {
+			return $this->parseError('No end parenthesis at end of CAST function', $parseString);
+		}
+		return $castDefinition;
 	}
 
 	/**
-	 * Compile field definition
+	 * Parsing the tablenames in the "FROM [$parseString] WHERE" part of a query into an array.
+	 * The success of this parsing determines if that part of the query is supported by TYPO3.
 	 *
-	 * @param array $fieldCfg Field definition parts
-	 * @return string Field definition string
+	 * @param string $parseString List of tables, eg. "pages, tt_content" or "pages A, pages B". NOTICE: passed by reference!
+	 * @param string $stopRegex Regular expressing to STOP parsing, eg. '^(WHERE)([[:space:]]*)'
+	 * @return array If successful parsing, returns an array, otherwise an error string.
+	 * @see compileFromTables()
 	 */
-	public function compileFieldCfg($fieldCfg) {
-		$cfg = '';
-		switch ((string)$this->databaseConnection->handlerCfg[$this->databaseConnection->lastHandlerKey]['type']) {
-			case 'native':
-				$cfg = parent::compileFieldCfg($fieldCfg);
-				break;
-			case 'adodb':
-				// Set type:
-				$type = $this->databaseConnection->getSpecifics()->getMetaFieldType($fieldCfg['fieldType']);
-				$cfg = $type;
-				// Add value, if any:
-				if ((string)$fieldCfg['value'] !== '' && in_array($type, array('C', 'C2'))) {
-					$cfg .= ' ' . $fieldCfg['value'];
-				} elseif (!isset($fieldCfg['value']) && in_array($type, array('C', 'C2'))) {
-					$cfg .= ' 255';
-				}
-				// Add additional features:
-				$noQuote = TRUE;
-				if (is_array($fieldCfg['featureIndex'])) {
-					// MySQL assigns DEFAULT value automatically if NOT NULL, fake this here
-					// numeric fields get 0 as default, other fields an empty string
-					if (isset($fieldCfg['featureIndex']['NOTNULL']) && !isset($fieldCfg['featureIndex']['DEFAULT']) && !isset($fieldCfg['featureIndex']['AUTO_INCREMENT'])) {
-						switch ($type) {
-							case 'I8':
-
-							case 'F':
-
-							case 'N':
-								$fieldCfg['featureIndex']['DEFAULT'] = array('keyword' => 'DEFAULT', 'value' => array('0', ''));
-								break;
-							default:
-								$fieldCfg['featureIndex']['DEFAULT'] = array('keyword' => 'DEFAULT', 'value' => array('', '\''));
+	public function parseFromTables(&$parseString, $stopRegex = '') {
+		// Prepare variables:
+		$parseString = $this->trimSQL($parseString);
+		$this->lastStopKeyWord = '';
+		$this->parse_error = '';
+		// Contains the parsed content
+		$stack = array();
+		// Pointer to positions in $stack
+		$pnt = 0;
+		// Recursivity brake.
+		$loopExit = 0;
+		// $parseString is continously shortend by the process and we keep parsing it till it is zero:
+		while ($parseString !== '') {
+			// Looking for the table:
+			if ($stack[$pnt]['table'] = $this->nextPart($parseString, '^([[:alnum:]_]+)(,|[[:space:]]+)')) {
+				// Looking for stop-keywords before fetching potential table alias:
+				if ($stopRegex && ($this->lastStopKeyWord = $this->nextPart($parseString, $stopRegex))) {
+					$this->lastStopKeyWord = $this->normalizeKeyword($this->lastStopKeyWord);
+					return $stack;
+				}
+				if (!preg_match('/^(LEFT|RIGHT|JOIN|INNER)[[:space:]]+/i', $parseString)) {
+					$stack[$pnt]['as_keyword'] = $this->nextPart($parseString, '^(AS[[:space:]]+)');
+					$stack[$pnt]['as'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]*');
+				}
+			} else {
+				return $this->parseError('No table name found as expected in parseFromTables()!', $parseString);
+			}
+			// Looking for JOIN
+			$joinCnt = 0;
+			while ($join = $this->nextPart($parseString, '^(((INNER|(LEFT|RIGHT)([[:space:]]+OUTER)?)[[:space:]]+)?JOIN)[[:space:]]+')) {
+				$stack[$pnt]['JOIN'][$joinCnt]['type'] = $join;
+				if ($stack[$pnt]['JOIN'][$joinCnt]['withTable'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+', 1)) {
+					if (!preg_match('/^ON[[:space:]]+/i', $parseString)) {
+						$stack[$pnt]['JOIN'][$joinCnt]['as_keyword'] = $this->nextPart($parseString, '^(AS[[:space:]]+)');
+						$stack[$pnt]['JOIN'][$joinCnt]['as'] = $this->nextPart($parseString, '^([[:alnum:]_]+)[[:space:]]+');
+					}
+					if (!$this->nextPart($parseString, '^(ON[[:space:]]+)')) {
+						return $this->parseError('No join condition found in parseFromTables()!', $parseString);
+					}
+					$stack[$pnt]['JOIN'][$joinCnt]['ON'] = array();
+					$condition = array('operator' => '');
+					$parseCondition = TRUE;
+					while ($parseCondition) {
+						if (($fieldName = $this->nextPart($parseString, '^([[:alnum:]._]+)[[:space:]]*(<=|>=|<|>|=|!=)')) !== '') {
+							// Parse field name into field and table:
+							$tableField = explode('.', $fieldName, 2);
+							$condition['left'] = array();
+							if (count($tableField) === 2) {
+								$condition['left']['table'] = $tableField[0];
+								$condition['left']['field'] = $tableField[1];
+							} else {
+								$condition['left']['table'] = '';
+								$condition['left']['field'] = $tableField[0];
+							}
+						} elseif (preg_match('/^CAST[[:space:]]*[(]/i', $parseString)) {
+							$condition['left'] = $this->parseCastStatement($parseString);
+							// Return the parse error
+							if (!is_array($condition['left'])) {
+								return $condition['left'];
+							}
+						} else {
+							return $this->parseError('No join field found in parseFromTables()!', $parseString);
+						}
+						// Find "comparator":
+						$condition['comparator'] = $this->nextPart($parseString, '^(<=|>=|<|>|=|!=)');
+						if (preg_match('/^CAST[[:space:]]*[(]/i', $parseString)) {
+							$condition['right'] = $this->parseCastStatement($parseString);
+							// Return the parse error
+							if (!is_array($condition['right'])) {
+								return $condition['right'];
+							}
+						} elseif (($fieldName = $this->nextPart($parseString, '^([[:alnum:]._]+)')) !== '') {
+							// Parse field name into field and table:
+							$tableField = explode('.', $fieldName, 2);
+							$condition['right'] = array();
+							if (count($tableField) === 2) {
+								$condition['right']['table'] = $tableField[0];
+								$condition['right']['field'] = $tableField[1];
+							} else {
+								$condition['right']['table'] = '';
+								$condition['right']['field'] = $tableField[0];
+							}
+						} elseif ($value = $this->getValue($parseString)) {
+							$condition['right']['value'] = $value;
+						} else {
+							return $this->parseError('No join field found in parseFromTables()!', $parseString);
 						}
+						$stack[$pnt]['JOIN'][$joinCnt]['ON'][] = $condition;
+						if (($operator = $this->nextPart($parseString, '^(AND|OR)')) !== '') {
+							$condition = array('operator' => $operator);
+						} else {
+							$parseCondition = FALSE;
+						}
+					}
+					$joinCnt++;
+				} else {
+					return $this->parseError('No join table found in parseFromTables()!', $parseString);
+				}
+			}
+			// Looking for stop-keywords:
+			if ($stopRegex && ($this->lastStopKeyWord = $this->nextPart($parseString, $stopRegex))) {
+				$this->lastStopKeyWord = $this->normalizeKeyword($this->lastStopKeyWord);
+				return $stack;
+			}
+			// Looking for comma:
+			if ($parseString !== '' && !$this->nextPart($parseString, '^(,)')) {
+				return $this->parseError('No comma found as expected in parseFromTables()', $parseString);
+			}
+			// Increasing pointer:
+			$pnt++;
+			// Check recursivity brake:
+			$loopExit++;
+			if ($loopExit > 500) {
+				return $this->parseError('More than 500 loops, exiting prematurely in parseFromTables()...', $parseString);
+			}
+		}
+		// Return result array:
+		return $stack;
+	}
+
+	/**
+	 * Parsing the WHERE clause fields in the "WHERE [$parseString] ..." part of a query into a multidimensional array.
+	 * The success of this parsing determines if that part of the query is supported by TYPO3.
+	 *
+	 * @param string $parseString WHERE clause to parse. NOTICE: passed by reference!
+	 * @param string $stopRegex Regular expressing to STOP parsing, eg. '^(GROUP BY|ORDER BY|LIMIT)([[:space:]]*)'
+	 * @param array $parameterReferences Array holding references to either named (:name) or question mark (?) parameters found
+	 * @return mixed If successful parsing, returns an array, otherwise an error string.
+	 */
+	public function parseWhereClause(&$parseString, $stopRegex = '', array &$parameterReferences = array()) {
+		// Prepare variables:
+		$parseString = $this->trimSQL($parseString);
+		$this->lastStopKeyWord = '';
+		$this->parse_error = '';
+		// Contains the parsed content
+		$stack = array(0 => array());
+		// Pointer to positions in $stack
+		$pnt = array(0 => 0);
+		// Determines parenthesis level
+		$level = 0;
+		// Recursivity brake.
+		$loopExit = 0;
+		// $parseString is continuously shortened by the process and we keep parsing it till it is zero:
+		while ($parseString !== '') {
+			// Look for next parenthesis level:
+			$newLevel = $this->nextPart($parseString, '^([(])');
+			// If new level is started, manage stack/pointers:
+			if ($newLevel === '(') {
+				// Increase level
+				$level++;
+				// Reset pointer for this level
+				$pnt[$level] = 0;
+				// Reset stack for this level
+				$stack[$level] = array();
+			} else {
+				// If no new level is started, just parse the current level:
+				// Find "modifier", eg. "NOT or !"
+				$stack[$level][$pnt[$level]]['modifier'] = trim($this->nextPart($parseString, '^(!|NOT[[:space:]]+)'));
+				// See if condition is EXISTS with a subquery
+				if (preg_match('/^EXISTS[[:space:]]*[(]/i', $parseString)) {
+					$stack[$level][$pnt[$level]]['func']['type'] = $this->nextPart($parseString, '^(EXISTS)[[:space:]]*');
+					// Strip off "("
+					$parseString = trim(substr($parseString, 1));
+					$stack[$level][$pnt[$level]]['func']['subquery'] = $this->parseSELECT($parseString, $parameterReferences);
+					// Seek to new position in parseString after parsing of the subquery
+					$parseString = $stack[$level][$pnt[$level]]['func']['subquery']['parseString'];
+					unset($stack[$level][$pnt[$level]]['func']['subquery']['parseString']);
+					if (!$this->nextPart($parseString, '^([)])')) {
+						return 'No ) parenthesis at end of subquery';
 					}
-					foreach ($fieldCfg['featureIndex'] as $feature => $featureDef) {
-						switch (TRUE) {
-							case $feature === 'UNSIGNED' && !$this->databaseConnection->runningADOdbDriver('mysql'):
-							case $feature === 'NOTNULL' && $this->databaseConnection->runningADOdbDriver('oci8'):
-								continue;
-							case $feature === 'AUTO_INCREMENT':
-								$cfg .= ' AUTOINCREMENT';
-								break;
-							case $feature === 'NOTNULL':
-								$cfg .= ' NOTNULL';
-								break;
-							default:
-								$cfg .= ' ' . $featureDef['keyword'];
+				} else {
+					// See if LOCATE function is found
+					if (preg_match('/^LOCATE[[:space:]]*[(]/i', $parseString)) {
+						$stack[$level][$pnt[$level]]['func']['type'] = $this->nextPart($parseString, '^(LOCATE)[[:space:]]*');
+						// Strip off "("
+						$parseString = trim(substr($parseString, 1));
+						$stack[$level][$pnt[$level]]['func']['substr'] = $this->getValue($parseString);
+						if (!$this->nextPart($parseString, '^(,)')) {
+							return $this->parseError('No comma found as expected in parseWhereClause()', $parseString);
+						}
+						if ($fieldName = $this->nextPart($parseString, '^([[:alnum:]\\*._]+)[[:space:]]*')) {
+							// Parse field name into field and table:
+							$tableField = explode('.', $fieldName, 2);
+							if (count($tableField) === 2) {
+								$stack[$level][$pnt[$level]]['func']['table'] = $tableField[0];
+								$stack[$level][$pnt[$level]]['func']['field'] = $tableField[1];
+							} else {
+								$stack[$level][$pnt[$level]]['func']['table'] = '';
+								$stack[$level][$pnt[$level]]['func']['field'] = $tableField[0];
+							}
+						} else {
+							return $this->parseError('No field name found as expected in parseWhereClause()', $parseString);
+						}
+						if ($this->nextPart($parseString, '^(,)')) {
+							$stack[$level][$pnt[$level]]['func']['pos'] = $this->getValue($parseString);
+						}
+						if (!$this->nextPart($parseString, '^([)])')) {
+							return $this->parseError('No ) parenthesis at end of function', $parseString);
+						}
+					} elseif (preg_match('/^IFNULL[[:space:]]*[(]/i', $parseString)) {
+						$stack[$level][$pnt[$level]]['func']['type'] = $this->nextPart($parseString, '^(IFNULL)[[:space:]]*');
+						$parseString = trim(substr($parseString, 1));
+						// Strip off "("
+						if ($fieldName = $this->nextPart($parseString, '^([[:alnum:]\\*._]+)[[:space:]]*')) {
+							// Parse field name into field and table:
+							$tableField = explode('.', $fieldName, 2);
+							if (count($tableField) === 2) {
+								$stack[$level][$pnt[$level]]['func']['table'] = $tableField[0];
+								$stack[$level][$pnt[$level]]['func']['field'] = $tableField[1];
+							} else {
+								$stack[$level][$pnt[$level]]['func']['table'] = '';
+								$stack[$level][$pnt[$level]]['func']['field'] = $tableField[0];
+							}
+						} else {
+							return $this->parseError('No field name found as expected in parseWhereClause()', $parseString);
+						}
+						if ($this->nextPart($parseString, '^(,)')) {
+							$stack[$level][$pnt[$level]]['func']['default'] = $this->getValue($parseString);
+						}
+						if (!$this->nextPart($parseString, '^([)])')) {
+							return $this->parseError('No ) parenthesis at end of function', $parseString);
+						}
+					} elseif (preg_match('/^CAST[[:space:]]*[(]/i', $parseString)) {
+						$stack[$level][$pnt[$level]]['func']['type'] = $this->nextPart($parseString, '^(CAST)[[:space:]]*');
+						$parseString = trim(substr($parseString, 1));
+						// Strip off "("
+						if ($fieldName = $this->nextPart($parseString, '^([[:alnum:]\\*._]+)[[:space:]]*')) {
+							// Parse field name into field and table:
+							$tableField = explode('.', $fieldName, 2);
+							if (count($tableField) === 2) {
+								$stack[$level][$pnt[$level]]['func']['table'] = $tableField[0];
+								$stack[$level][$pnt[$level]]['func']['field'] = $tableField[1];
+							} else {
+								$stack[$level][$pnt[$level]]['func']['table'] = '';
+								$stack[$level][$pnt[$level]]['func']['field'] = $tableField[0];
+							}
+						} else {
+							return $this->parseError('No field name found as expected in parseWhereClause()', $parseString);
+						}
+						if ($this->nextPart($parseString, '^([[:space:]]*AS[[:space:]]*)')) {
+							$stack[$level][$pnt[$level]]['func']['datatype'] = $this->getValue($parseString);
+						}
+						if (!$this->nextPart($parseString, '^([)])')) {
+							return $this->parseError('No ) parenthesis at end of function', $parseString);
+						}
+					} elseif (preg_match('/^FIND_IN_SET[[:space:]]*[(]/i', $parseString)) {
+						$stack[$level][$pnt[$level]]['func']['type'] = $this->nextPart($parseString, '^(FIND_IN_SET)[[:space:]]*');
+						// Strip off "("
+						$parseString = trim(substr($parseString, 1));
+						if ($str = $this->getValue($parseString)) {
+							$stack[$level][$pnt[$level]]['func']['str'] = $str;
+							if ($fieldName = $this->nextPart($parseString, '^,[[:space:]]*([[:alnum:]._]+)[[:space:]]*', TRUE)) {
+								// Parse field name into field and table:
+								$tableField = explode('.', $fieldName, 2);
+								if (count($tableField) === 2) {
+									$stack[$level][$pnt[$level]]['func']['table'] = $tableField[0];
+									$stack[$level][$pnt[$level]]['func']['field'] = $tableField[1];
+								} else {
+									$stack[$level][$pnt[$level]]['func']['table'] = '';
+									$stack[$level][$pnt[$level]]['func']['field'] = $tableField[0];
+								}
+							} else {
+								return $this->parseError('No field name found as expected in parseWhereClause()', $parseString);
+							}
+							if (!$this->nextPart($parseString, '^([)])')) {
+								return $this->parseError('No ) parenthesis at end of function', $parseString);
+							}
+						} else {
+							return $this->parseError('No item to look for found as expected in parseWhereClause()', $parseString);
+						}
+					} else {
+						// Support calculated value only for:
+						// - "&" (boolean AND)
+						// - "+" (addition)
+						// - "-" (substraction)
+						// - "*" (multiplication)
+						// - "/" (division)
+						// - "%" (modulo)
+						$calcOperators = '&|\\+|-|\\*|\\/|%';
+						// Fieldname:
+						if (($fieldName = $this->nextPart($parseString, '^([[:alnum:]._]+)([[:space:]]+|' . $calcOperators . '|<=|>=|<|>|=|!=|IS)')) !== '') {
+							// Parse field name into field and table:
+							$tableField = explode('.', $fieldName, 2);
+							if (count($tableField) === 2) {
+								$stack[$level][$pnt[$level]]['table'] = $tableField[0];
+								$stack[$level][$pnt[$level]]['field'] = $tableField[1];
+							} else {
+								$stack[$level][$pnt[$level]]['table'] = '';
+								$stack[$level][$pnt[$level]]['field'] = $tableField[0];
+							}
+						} else {
+							return $this->parseError('No field name found as expected in parseWhereClause()', $parseString);
+						}
+						// See if the value is calculated:
+						$stack[$level][$pnt[$level]]['calc'] = $this->nextPart($parseString, '^(' . $calcOperators . ')');
+						if ((string)$stack[$level][$pnt[$level]]['calc'] !== '') {
+							// Finding value for calculation:
+							$calc_value = $this->getValue($parseString);
+							$stack[$level][$pnt[$level]]['calc_value'] = $calc_value;
+							if (count($calc_value) === 1 && is_string($calc_value[0])) {
+								// Value is a field, store it to allow DBAL to post-process it (quoting, remapping)
+								$tableField = explode('.', $calc_value[0], 2);
+								if (count($tableField) === 2) {
+									$stack[$level][$pnt[$level]]['calc_table'] = $tableField[0];
+									$stack[$level][$pnt[$level]]['calc_field'] = $tableField[1];
+								} else {
+									$stack[$level][$pnt[$level]]['calc_table'] = '';
+									$stack[$level][$pnt[$level]]['calc_field'] = $tableField[0];
+								}
+							}
 						}
-						// Add value if found:
-						if (is_array($featureDef['value'])) {
-							if ($featureDef['value'][0] === '') {
-								$cfg .= ' "\'\'"';
+					}
+					$stack[$level][$pnt[$level]]['comparator'] = $this->nextPart($parseString, '^(' . implode('|', self::$comparatorPatterns) . ')');
+					if ($stack[$level][$pnt[$level]]['comparator'] !== '') {
+						if (preg_match('/^CONCAT[[:space:]]*\\(/', $parseString)) {
+							$this->nextPart($parseString, '^(CONCAT[[:space:]]?[(])');
+							$values = array(
+								'operator' => 'CONCAT',
+								'args' => array()
+							);
+							$cnt = 0;
+							while ($fieldName = $this->nextPart($parseString, '^([[:alnum:]._]+)')) {
+								// Parse field name into field and table:
+								$tableField = explode('.', $fieldName, 2);
+								if (count($tableField) === 2) {
+									$values['args'][$cnt]['table'] = $tableField[0];
+									$values['args'][$cnt]['field'] = $tableField[1];
+								} else {
+									$values['args'][$cnt]['table'] = '';
+									$values['args'][$cnt]['field'] = $tableField[0];
+								}
+								// Looking for comma:
+								$this->nextPart($parseString, '^(,)');
+								$cnt++;
+							}
+							// Look for ending parenthesis:
+							$this->nextPart($parseString, '([)])');
+							$stack[$level][$pnt[$level]]['value'] = $values;
+						} else {
+							if (GeneralUtility::inList('IN,NOT IN', $stack[$level][$pnt[$level]]['comparator']) && preg_match('/^[(][[:space:]]*SELECT[[:space:]]+/', $parseString)) {
+								$this->nextPart($parseString, '^([(])');
+								$stack[$level][$pnt[$level]]['subquery'] = $this->parseSELECT($parseString, $parameterReferences);
+								// Seek to new position in parseString after parsing of the subquery
+								if (!empty($stack[$level][$pnt[$level]]['subquery']['parseString'])) {
+									$parseString = $stack[$level][$pnt[$level]]['subquery']['parseString'];
+									unset($stack[$level][$pnt[$level]]['subquery']['parseString']);
+								}
+								if (!$this->nextPart($parseString, '^([)])')) {
+									return 'No ) parenthesis at end of subquery';
+								}
 							} else {
-								$cfg .= ' ' . $featureDef['value'][1] . $this->compileAddslashes($featureDef['value'][0]) . $featureDef['value'][1];
-								if (!is_numeric($featureDef['value'][0])) {
-									$noQuote = FALSE;
+								if (GeneralUtility::inList('BETWEEN,NOT BETWEEN', $stack[$level][$pnt[$level]]['comparator'])) {
+									$stack[$level][$pnt[$level]]['values'] = array();
+									$stack[$level][$pnt[$level]]['values'][0] = $this->getValue($parseString);
+									if (!$this->nextPart($parseString, '^(AND)')) {
+										return $this->parseError('No AND operator found as expected in parseWhereClause()', $parseString);
+									}
+									$stack[$level][$pnt[$level]]['values'][1] = $this->getValue($parseString);
+								} else {
+									// Finding value for comparator:
+									$stack[$level][$pnt[$level]]['value'] = &$this->getValueOrParameter($parseString, $stack[$level][$pnt[$level]]['comparator'], '', $parameterReferences);
+									if ($this->parse_error) {
+										return $this->parse_error;
+									}
 								}
 							}
 						}
 					}
 				}
-				if ($noQuote) {
-					$cfg .= ' NOQUOTE';
+				// Finished, increase pointer:
+				$pnt[$level]++;
+				// Checking if we are back to level 0 and we should still decrease level,
+				// meaning we were probably parsing as subquery and should return here:
+				if ($level === 0 && preg_match('/^[)]/', $parseString)) {
+					// Return the stacks lowest level:
+					return $stack[0];
 				}
-				break;
+				// Checking if we are back to level 0 and we should still decrease level,
+				// meaning we were probably parsing a subquery and should return here:
+				if ($level === 0 && preg_match('/^[)]/', $parseString)) {
+					// Return the stacks lowest level:
+					return $stack[0];
+				}
+				// Checking if the current level is ended, in that case do stack management:
+				while ($this->nextPart($parseString, '^([)])')) {
+					$level--;
+					// Decrease level:
+					// Copy stack
+					$stack[$level][$pnt[$level]]['sub'] = $stack[$level + 1];
+					// Increase pointer of the new level
+					$pnt[$level]++;
+					// Make recursivity check:
+					$loopExit++;
+					if ($loopExit > 500) {
+						return $this->parseError('More than 500 loops (in search for exit parenthesis), exiting prematurely in parseWhereClause()...', $parseString);
+					}
+				}
+				// Detecting the operator for the next level:
+				$op = $this->nextPart($parseString, '^(AND[[:space:]]+NOT|&&[[:space:]]+NOT|OR[[:space:]]+NOT|OR[[:space:]]+NOT|\\|\\|[[:space:]]+NOT|AND|&&|OR|\\|\\|)(\\(|[[:space:]]+)');
+				if ($op) {
+					// Normalize boolean operator
+					$op = str_replace(array('&&', '||'), array('AND', 'OR'), $op);
+					$stack[$level][$pnt[$level]]['operator'] = $op;
+				} elseif ($parseString !== '') {
+					// Looking for stop-keywords:
+					if ($stopRegex && ($this->lastStopKeyWord = $this->nextPart($parseString, $stopRegex))) {
+						$this->lastStopKeyWord = $this->normalizeKeyword($this->lastStopKeyWord);
+						return $stack[0];
+					} else {
+						return $this->parseError('No operator, but parsing not finished in parseWhereClause().', $parseString);
+					}
+				}
+			}
+			// Make recursivity check:
+			$loopExit++;
+			if ($loopExit > 500) {
+				return $this->parseError('More than 500 loops, exiting prematurely in parseWhereClause()...', $parseString);
+			}
+		}
+		// Return the stacks lowest level:
+		return $stack[0];
+	}
+
+	/**
+	 * Parsing the WHERE clause fields in the "WHERE [$parseString] ..." part of a query into a multidimensional array.
+	 * The success of this parsing determines if that part of the query is supported by TYPO3.
+	 *
+	 * @param string $parseString WHERE clause to parse. NOTICE: passed by reference!
+	 * @param string $stopRegex Regular expressing to STOP parsing, eg. '^(GROUP BY|ORDER BY|LIMIT)([[:space:]]*)'
+	 * @return mixed If successful parsing, returns an array, otherwise an error string.
+	 */
+	public function parseFieldDef(&$parseString, $stopRegex = '') {
+		// Prepare variables:
+		$parseString = $this->trimSQL($parseString);
+		$this->lastStopKeyWord = '';
+		$this->parse_error = '';
+		$result = array();
+		// Field type:
+		if ($result['fieldType'] = $this->nextPart($parseString, '^(int|smallint|tinyint|mediumint|bigint|double|numeric|decimal|float|varchar|char|text|tinytext|mediumtext|longtext|blob|tinyblob|mediumblob|longblob)([[:space:],]+|\\()')) {
+			// Looking for value:
+			if ($parseString[0] === '(') {
+				$parseString = substr($parseString, 1);
+				if ($result['value'] = $this->nextPart($parseString, '^([^)]*)')) {
+					$parseString = ltrim(substr($parseString, 1));
+				} else {
+					return $this->parseError('No end-parenthesis for value found in parseFieldDef()!', $parseString);
+				}
+			}
+			// Looking for keywords
+			while ($keyword = $this->nextPart($parseString, '^(DEFAULT|NOT[[:space:]]+NULL|AUTO_INCREMENT|UNSIGNED)([[:space:]]+|,|\\))')) {
+				$keywordCmp = $this->normalizeKeyword($keyword);
+				$result['featureIndex'][$keywordCmp]['keyword'] = $keyword;
+				switch ($keywordCmp) {
+					case 'DEFAULT':
+						$result['featureIndex'][$keywordCmp]['value'] = $this->getValue($parseString);
+						break;
+				}
+			}
+		} else {
+			return $this->parseError('Field type unknown in parseFieldDef()!', $parseString);
 		}
-		// Return field definition string:
-		return $cfg;
+		return $result;
 	}
 
 	/**
@@ -490,6 +1473,227 @@ class SqlParser extends \TYPO3\CMS\Core\Database\SqlParser {
 		return !is_numeric($featureIndex['DEFAULT']['value'][0]) && empty($featureIndex['DEFAULT']['value'][0]);
 	}
 
+	/************************************
+	 *
+	 * Parsing: Helper functions
+	 *
+	 ************************************/
+	/**
+	 * Strips off a part of the parseString and returns the matching part.
+	 * Helper function for the parsing methods.
+	 *
+	 * @param string $parseString Parse string; if $regex finds anything the value of the first () level will be stripped of the string in the beginning. Further $parseString is left-trimmed (on success). Notice; parsestring is passed by reference.
+	 * @param string $regex Regex to find a matching part in the beginning of the string. Rules: You MUST start the regex with "^" (finding stuff in the beginning of string) and the result of the first parenthesis is what will be returned to you (and stripped of the string). Eg. '^(AND|OR|&&)[[:space:]]+' will return AND, OR or && if found and having one of more whitespaces after it, plus shorten $parseString with that match and any space after (by ltrim())
+	 * @param bool $trimAll If set the full match of the regex is stripped of the beginning of the string!
+	 * @return string The value of the first parenthesis level of the REGEX.
+	 */
+	protected function nextPart(&$parseString, $regex, $trimAll = FALSE) {
+		$reg = array();
+		// Adding space char because [[:space:]]+ is often a requirement in regex's
+		if (preg_match('/' . $regex . '/i', $parseString . ' ', $reg)) {
+			$parseString = ltrim(substr($parseString, strlen($reg[$trimAll ? 0 : 1])));
+			return $reg[1];
+		}
+		// No match found
+		return '';
+	}
+
+	/**
+	 * Normalizes the keyword by removing any separator and changing to uppercase
+	 *
+	 * @param string $keyword The keyword being normalized
+	 * @return string
+	 */
+	public static function normalizeKeyword($keyword) {
+		return strtoupper(str_replace(self::$interQueryWhitespaces, '', $keyword));
+	}
+
+	/**
+	 * Finds value or either named (:name) or question mark (?) parameter markers at the beginning
+	 * of $parseString, returns result and strips it of parseString.
+	 * This method returns a pointer to the parameter or value that was found. In case of a parameter
+	 * the pointer is a reference to the corresponding item in array $parameterReferences.
+	 *
+	 * @param string $parseString The parseString
+	 * @param string $comparator The comparator used before.
+	 * @param string $mode The mode, e.g., "INDEX
+	 * @param mixed The value (string/integer) or parameter (:name/?). Otherwise an array with error message in first key (0)
+	 */
+	protected function &getValueOrParameter(&$parseString, $comparator = '', $mode = '', array &$parameterReferences = array()) {
+		$parameter = $this->nextPart($parseString, '^(\\:[[:alnum:]_]+|\\?)');
+		if ($parameter === '?') {
+			if (!isset($parameterReferences['?'])) {
+				$parameterReferences['?'] = array();
+			}
+			$value = array('?');
+			$parameterReferences['?'][] = &$value;
+		} elseif ($parameter !== '') {
+			// named parameter
+			if (isset($parameterReferences[$parameter])) {
+				// Use the same reference as last time we encountered this parameter
+				$value = &$parameterReferences[$parameter];
+			} else {
+				$value = array($parameter);
+				$parameterReferences[$parameter] = &$value;
+			}
+		} else {
+			$value = $this->getValue($parseString, $comparator, $mode);
+		}
+		return $value;
+	}
+
+	/**
+	 * Finds value in beginning of $parseString, returns result and strips it of parseString
+	 *
+	 * @param string $parseString The parseString, eg. "(0,1,2,3) ..." or "('asdf','qwer') ..." or "1234 ..." or "'My string value here' ...
+	 * @param string $comparator The comparator used before. If "NOT IN" or "IN" then the value is expected to be a list of values. Otherwise just an integer (un-quoted) or string (quoted)
+	 * @param string $mode The mode, eg. "INDEX
+	 * @return mixed The value (string/integer). Otherwise an array with error message in first key (0)
+	 */
+	protected function getValue(&$parseString, $comparator = '', $mode = '') {
+		$value = '';
+		if (GeneralUtility::inList('NOTIN,IN,_LIST', strtoupper(str_replace(array(' ', LF, CR, TAB), '', $comparator)))) {
+			// List of values:
+			if ($this->nextPart($parseString, '^([(])')) {
+				$listValues = array();
+				$comma = ',';
+				while ($comma === ',') {
+					$listValues[] = $this->getValue($parseString);
+					if ($mode === 'INDEX') {
+						// Remove any length restriction on INDEX definition
+						$this->nextPart($parseString, '^([(]\\d+[)])');
+					}
+					$comma = $this->nextPart($parseString, '^([,])');
+				}
+				$out = $this->nextPart($parseString, '^([)])');
+				if ($out) {
+					if ($comparator === '_LIST') {
+						$kVals = array();
+						foreach ($listValues as $vArr) {
+							$kVals[] = $vArr[0];
+						}
+						return $kVals;
+					} else {
+						return $listValues;
+					}
+				} else {
+					return array($this->parseError('No ) parenthesis in list', $parseString));
+				}
+			} else {
+				return array($this->parseError('No ( parenthesis starting the list', $parseString));
+			}
+		} else {
+			// Just plain string value, in quotes or not:
+			// Quote?
+			$firstChar = $parseString[0];
+			switch ($firstChar) {
+				case '"':
+					$value = array($this->getValueInQuotes($parseString, '"'), '"');
+					break;
+				case '\'':
+					$value = array($this->getValueInQuotes($parseString, '\''), '\'');
+					break;
+				default:
+					$reg = array();
+					if (preg_match('/^([[:alnum:]._-]+(?:\\([0-9]+\\))?)/i', $parseString, $reg)) {
+						$parseString = ltrim(substr($parseString, strlen($reg[0])));
+						$value = array($reg[1]);
+					}
+			}
+		}
+		return $value;
+	}
+
+	/**
+	 * Strip slashes function used for parsing
+	 * NOTICE: If a query being parsed was prepared for another database than MySQL this function should probably be changed
+	 *
+	 * @param string $str Input string
+	 * @return string Output string
+	 */
+	protected function parseStripslashes($str) {
+		$search = array('\\\\', '\\\'', '\\"', '\0', '\n', '\r', '\Z');
+		$replace = array('\\', '\'', '"', "\x00", "\x0a", "\x0d", "\x1a");
+
+		return str_replace($search, $replace, $str);
+	}
+
+	/**
+	 * Setting the internal error message value, $this->parse_error and returns that value.
+	 *
+	 * @param string $msg Input error message
+	 * @param string $restQuery Remaining query to parse.
+	 * @return string Error message.
+	 */
+	protected function parseError($msg, $restQuery) {
+		$this->parse_error = 'SQL engine parse ERROR: ' . $msg . ': near "' . substr($restQuery, 0, 50) . '"';
+		return $this->parse_error;
+	}
+
+	/**
+	 * Trimming SQL as preparation for parsing.
+	 * ";" in the end is stripped off.
+	 * White space is trimmed away around the value
+	 * A single space-char is added in the end
+	 *
+	 * @param string $str Input string
+	 * @return string Output string
+	 */
+	protected function trimSQL($str) {
+		return rtrim(rtrim(trim($str), ';')) . ' ';
+	}
+
+	/*************************
+	 *
+	 * Compiling queries
+	 *
+	 *************************/
+
+	/**
+	 * Compiles an SQL query from components
+	 *
+	 * @param array $components Array of SQL query components
+	 * @return string SQL query
+	 * @see parseSQL()
+	 */
+	public function compileSQL($components) {
+		return $this->getSqlCompiler()->compileSQL($components);
+	}
+
+	/**
+	 * Compiles a "SELECT [output] FROM..:" field list based on input array (made with ->parseFieldList())
+	 * Can also compile field lists for ORDER BY and GROUP BY.
+	 *
+	 * @param array $selectFields Array of select fields, (made with ->parseFieldList())
+	 * @param bool $compileComments Whether comments should be compiled
+	 * @return string Select field string
+	 * @see parseFieldList()
+	 */
+	public function compileFieldList($selectFields, $compileComments = TRUE) {
+		return $this->getSqlCompiler()->compileFieldList($selectFields, $compileComments);
+	}
+
+	/**
+	 * Compiles a "FROM [output] WHERE..:" table list based on input array (made with ->parseFromTables())
+	 *
+	 * @param array $tablesArray Array of table names, (made with ->parseFromTables())
+	 * @return string Table name string
+	 * @see parseFromTables()
+	 */
+	public function compileFromTables($tablesArray) {
+		return $this->getSqlCompiler()->compileFromTables($tablesArray);
+	}
+
+	/**
+	 * Compile field definition
+	 *
+	 * @param array $fieldCfg Field definition parts
+	 * @return string Field definition string
+	 */
+	public function compileFieldCfg($fieldCfg) {
+		return $this->getSqlCompiler()->compileFieldCfg($fieldCfg);
+	}
+
 	/**
 	 * Implodes an array of WHERE clause configuration into a WHERE clause.
 	 *
@@ -507,277 +1711,25 @@ class SqlParser extends \TYPO3\CMS\Core\Database\SqlParser {
 	 * @see \TYPO3\CMS\Core\Database\SqlParser::parseWhereClause()
 	 */
 	public function compileWhereClause($clauseArray, $functionMapping = TRUE) {
-		$output = '';
-		switch ((string)$this->databaseConnection->handlerCfg[$this->databaseConnection->lastHandlerKey]['type']) {
-			case 'native':
-				$output = parent::compileWhereClause($clauseArray);
-				break;
-			case 'adodb':
-				// Prepare buffer variable:
-				$output = '';
-				// Traverse clause array:
-				if (is_array($clauseArray)) {
-					foreach ($clauseArray as $v) {
-						// Set operator:
-						$output .= $v['operator'] ? ' ' . $v['operator'] : '';
-						// Look for sublevel:
-						if (is_array($v['sub'])) {
-							$output .= ' (' . trim($this->compileWhereClause($v['sub'], $functionMapping)) . ')';
-						} elseif (isset($v['func']) && $v['func']['type'] === 'EXISTS') {
-							$output .= ' ' . trim($v['modifier']) . ' EXISTS (' . $this->compileSELECT($v['func']['subquery']) . ')';
-						} else {
-							if (isset($v['func']) && $v['func']['type'] === 'LOCATE') {
-								$output .= ' ' . trim($v['modifier']);
-								switch (TRUE) {
-									case $this->databaseConnection->runningADOdbDriver('mssql') && $functionMapping:
-										$output .= ' CHARINDEX(';
-										$output .= $v['func']['substr'][1] . $v['func']['substr'][0] . $v['func']['substr'][1];
-										$output .= ', ' . ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
-										$output .= isset($v['func']['pos']) ? ', ' . $v['func']['pos'][0] : '';
-										$output .= ')';
-										break;
-									case $this->databaseConnection->runningADOdbDriver('oci8') && $functionMapping:
-										$output .= ' INSTR(';
-										$output .= ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
-										$output .= ', ' . $v['func']['substr'][1] . $v['func']['substr'][0] . $v['func']['substr'][1];
-										$output .= isset($v['func']['pos']) ? ', ' . $v['func']['pos'][0] : '';
-										$output .= ')';
-										break;
-									default:
-										$output .= ' LOCATE(';
-										$output .= $v['func']['substr'][1] . $v['func']['substr'][0] . $v['func']['substr'][1];
-										$output .= ', ' . ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
-										$output .= isset($v['func']['pos']) ? ', ' . $v['func']['pos'][0] : '';
-										$output .= ')';
-								}
-							} elseif (isset($v['func']) && $v['func']['type'] === 'IFNULL') {
-								$output .= ' ' . trim($v['modifier']) . ' ';
-								switch (TRUE) {
-									case $this->databaseConnection->runningADOdbDriver('mssql') && $functionMapping:
-										$output .= 'ISNULL';
-										break;
-									case $this->databaseConnection->runningADOdbDriver('oci8') && $functionMapping:
-										$output .= 'NVL';
-										break;
-									default:
-										$output .= 'IFNULL';
-								}
-								$output .= '(';
-								$output .= ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
-								$output .= ', ' . $v['func']['default'][1] . $this->compileAddslashes($v['func']['default'][0]) . $v['func']['default'][1];
-								$output .= ')';
-							} elseif (isset($v['func']) && $v['func']['type'] === 'FIND_IN_SET') {
-								$output .= ' ' . trim($v['modifier']) . ' ';
-								if ($functionMapping) {
-									switch (TRUE) {
-										case $this->databaseConnection->runningADOdbDriver('mssql'):
-											$field = ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
-											if (!isset($v['func']['str_like'])) {
-												$v['func']['str_like'] = $v['func']['str'][0];
-											}
-											$output .= '\',\'+' . $field . '+\',\' LIKE \'%,' . $v['func']['str_like'] . ',%\'';
-											break;
-										case $this->databaseConnection->runningADOdbDriver('oci8'):
-											$field = ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
-											if (!isset($v['func']['str_like'])) {
-												$v['func']['str_like'] = $v['func']['str'][0];
-											}
-											$output .= '\',\'||' . $field . '||\',\' LIKE \'%,' . $v['func']['str_like'] . ',%\'';
-											break;
-										case $this->databaseConnection->runningADOdbDriver('postgres'):
-											$output .= ' FIND_IN_SET(';
-											$output .= $v['func']['str'][1] . $v['func']['str'][0] . $v['func']['str'][1];
-											$output .= ', ' . ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
-											$output .= ') != 0';
-											break;
-										default:
-											$field = ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
-											if (!isset($v['func']['str_like'])) {
-												$v['func']['str_like'] = $v['func']['str'][0];
-											}
-											$output .= '(' . $field . ' LIKE \'%,' . $v['func']['str_like'] . ',%\'' . ' OR ' . $field . ' LIKE \'' . $v['func']['str_like'] . ',%\'' . ' OR ' . $field . ' LIKE \'%,' . $v['func']['str_like'] . '\'' . ' OR ' . $field . '= ' . $v['func']['str'][1] . $v['func']['str'][0] . $v['func']['str'][1] . ')';
-									}
-								} else {
-									switch (TRUE) {
-										case $this->databaseConnection->runningADOdbDriver('mssql'):
-
-										case $this->databaseConnection->runningADOdbDriver('oci8'):
-
-										case $this->databaseConnection->runningADOdbDriver('postgres'):
-											$output .= ' FIND_IN_SET(';
-											$output .= $v['func']['str'][1] . $v['func']['str'][0] . $v['func']['str'][1];
-											$output .= ', ' . ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
-											$output .= ')';
-											break;
-										default:
-											$field = ($v['func']['table'] ? $v['func']['table'] . '.' : '') . $v['func']['field'];
-											if (!isset($v['func']['str_like'])) {
-												$v['func']['str_like'] = $v['func']['str'][0];
-											}
-											$output .= '(' . $field . ' LIKE \'%,' . $v['func']['str_like'] . ',%\'' . ' OR ' . $field . ' LIKE \'' . $v['func']['str_like'] . ',%\'' . ' OR ' . $field . ' LIKE \'%,' . $v['func']['str_like'] . '\'' . ' OR ' . $field . '= ' . $v['func']['str'][1] . $v['func']['str'][0] . $v['func']['str'][1] . ')';
-									}
-								}
-							} else {
-								// Set field/table with modifying prefix if any:
-								$output .= ' ' . trim($v['modifier']) . ' ';
-								// DBAL-specific: Set calculation, if any:
-								if ($v['calc'] === '&' && $functionMapping) {
-									switch (TRUE) {
-										case $this->databaseConnection->runningADOdbDriver('oci8'):
-											// Oracle only knows BITAND(x,y) - sigh
-											$output .= 'BITAND(' . trim((($v['table'] ? $v['table'] . '.' : '') . $v['field'])) . ',' . $v['calc_value'][1] . $this->compileAddslashes($v['calc_value'][0]) . $v['calc_value'][1] . ')';
-											break;
-										default:
-											// MySQL, MS SQL Server, PostgreSQL support the &-syntax
-											$output .= trim((($v['table'] ? $v['table'] . '.' : '') . $v['field'])) . $v['calc'] . $v['calc_value'][1] . $this->compileAddslashes($v['calc_value'][0]) . $v['calc_value'][1];
-									}
-								} elseif ($v['calc']) {
-									$output .= trim((($v['table'] ? $v['table'] . '.' : '') . $v['field'])) . $v['calc'];
-									if (isset($v['calc_table'])) {
-										$output .= trim(($v['calc_table'] ? $v['calc_table'] . '.' : '') . $v['calc_field']);
-									} else {
-										$output .= $v['calc_value'][1] . $this->compileAddslashes($v['calc_value'][0]) . $v['calc_value'][1];
-									}
-								} elseif (!($this->databaseConnection->runningADOdbDriver('oci8') && preg_match('/(NOT )?LIKE( BINARY)?/', $v['comparator']) && $functionMapping)) {
-									$output .= trim(($v['table'] ? $v['table'] . '.' : '') . $v['field']);
-								}
-							}
-							// Set comparator:
-							if ($v['comparator']) {
-								$isLikeOperator = preg_match('/(NOT )?LIKE( BINARY)?/', $v['comparator']);
-								switch (TRUE) {
-									case $this->databaseConnection->runningADOdbDriver('oci8') && $isLikeOperator && $functionMapping:
-										// Oracle cannot handle LIKE on CLOB fields - sigh
-										if (isset($v['value']['operator'])) {
-											$values = array();
-											foreach ($v['value']['args'] as $fieldDef) {
-												$values[] = ($fieldDef['table'] ? $fieldDef['table'] . '.' : '') . $fieldDef['field'];
-											}
-											$compareValue = ' ' . $v['value']['operator'] . '(' . implode(',', $values) . ')';
-										} else {
-											$compareValue = $v['value'][1] . $this->compileAddslashes(trim($v['value'][0], '%')) . $v['value'][1];
-										}
-										if (GeneralUtility::isFirstPartOfStr($v['comparator'], 'NOT')) {
-											$output .= 'NOT ';
-										}
-										// To be on the safe side
-										$isLob = TRUE;
-										if ($v['table']) {
-											// Table and field names are quoted:
-											$tableName = substr($v['table'], 1, strlen($v['table']) - 2);
-											$fieldName = substr($v['field'], 1, strlen($v['field']) - 2);
-											$fieldType = $this->databaseConnection->sql_field_metatype($tableName, $fieldName);
-											$isLob = $fieldType === 'B' || $fieldType === 'XL';
-										}
-										if (strtoupper(substr($v['comparator'], -6)) === 'BINARY') {
-											if ($isLob) {
-												$output .= '(dbms_lob.instr(' . trim((($v['table'] ? $v['table'] . '.' : '') . $v['field'])) . ', ' . $compareValue . ',1,1) > 0)';
-											} else {
-												$output .= '(instr(' . trim((($v['table'] ? $v['table'] . '.' : '') . $v['field'])) . ', ' . $compareValue . ',1,1) > 0)';
-											}
-										} else {
-											if ($isLob) {
-												$output .= '(dbms_lob.instr(LOWER(' . trim((($v['table'] ? $v['table'] . '.' : '') . $v['field'])) . '), ' . GeneralUtility::strtolower($compareValue) . ',1,1) > 0)';
-											} else {
-												$output .= '(instr(LOWER(' . trim((($v['table'] ? $v['table'] . '.' : '') . $v['field'])) . '), ' . GeneralUtility::strtolower($compareValue) . ',1,1) > 0)';
-											}
-										}
-										break;
-									default:
-										if ($isLikeOperator && $functionMapping) {
-											if ($this->databaseConnection->runningADOdbDriver('postgres') || $this->databaseConnection->runningADOdbDriver('postgres64') || $this->databaseConnection->runningADOdbDriver('postgres7') || $this->databaseConnection->runningADOdbDriver('postgres8')) {
-												// Remap (NOT)? LIKE to (NOT)? ILIKE
-												// and (NOT)? LIKE BINARY to (NOT)? LIKE
-												switch ($v['comparator']) {
-													case 'LIKE':
-														$v['comparator'] = 'ILIKE';
-														break;
-													case 'NOT LIKE':
-														$v['comparator'] = 'NOT ILIKE';
-														break;
-													default:
-														$v['comparator'] = str_replace(' BINARY', '', $v['comparator']);
-												}
-											} else {
-												// No more BINARY operator
-												$v['comparator'] = str_replace(' BINARY', '', $v['comparator']);
-											}
-										}
-										$output .= ' ' . $v['comparator'];
-										// Detecting value type; list or plain:
-										$comparator = $this->normalizeKeyword($v['comparator']);
-										if (GeneralUtility::inList('NOTIN,IN', $comparator)) {
-											if (isset($v['subquery'])) {
-												$output .= ' (' . $this->compileSELECT($v['subquery']) . ')';
-											} else {
-												$valueBuffer = array();
-												foreach ($v['value'] as $realValue) {
-													$valueBuffer[] = $realValue[1] . $this->compileAddslashes($realValue[0]) . $realValue[1];
-												}
-
-												$dbmsSpecifics = $this->databaseConnection->getSpecifics();
-												if ($dbmsSpecifics === NULL) {
-													$output .= ' (' . trim(implode(',', $valueBuffer)) . ')';
-												} else {
-													$chunkedList = $dbmsSpecifics->splitMaxExpressions($valueBuffer);
-													$chunkCount = count($chunkedList);
-
-													if ($chunkCount === 1) {
-														$output .= ' (' . trim(implode(',', $valueBuffer)) . ')';
-													} else {
-														$listExpressions = array();
-														$field = trim(($v['table'] ? $v['table'] . '.' : '') . $v['field']);
-
-														switch ($comparator) {
-															case 'IN':
-																$operator = 'OR';
-																break;
-															case 'NOTIN':
-																$operator = 'AND';
-																break;
-															default:
-																$operator = '';
-														}
-
-														for ($i = 0; $i < $chunkCount; ++$i) {
-															$listPart = trim(implode(',', $chunkedList[$i]));
-															$listExpressions[] = ' (' . $listPart . ')';
-														}
-
-														$implodeString = ' ' . $operator . ' ' . $field . ' ' . $v['comparator'];
-
-														// add opening brace before field
-														$lastFieldPos = strrpos($output, $field);
-														$output = substr_replace($output, '(', $lastFieldPos, 0);
-														$output .= implode($implodeString, $listExpressions) . ')';
-													}
-												}
-											}
-										} elseif (GeneralUtility::inList('BETWEEN,NOT BETWEEN', $v['comparator'])) {
-											$lbound = $v['values'][0];
-											$ubound = $v['values'][1];
-											$output .= ' ' . $lbound[1] . $this->compileAddslashes($lbound[0]) . $lbound[1];
-											$output .= ' AND ';
-											$output .= $ubound[1] . $this->compileAddslashes($ubound[0]) . $ubound[1];
-										} elseif (isset($v['value']['operator'])) {
-											$values = array();
-											foreach ($v['value']['args'] as $fieldDef) {
-												$values[] = ($fieldDef['table'] ? $fieldDef['table'] . '.' : '') . $fieldDef['field'];
-											}
-											$output .= ' ' . $v['value']['operator'] . '(' . implode(',', $values) . ')';
-										} else {
-											$output .= ' ' . $v['value'][1] . $this->compileAddslashes($v['value'][0]) . $v['value'][1];
-										}
-								}
-							}
-						}
-					}
-				}
-				break;
+		return $this->getSqlCompiler()->compileWhereClause($clauseArray, $functionMapping);
+	}
+
+	/**
+	 * @return \TYPO3\CMS\Dbal\Database\SqlCompilers\Adodb|\TYPO3\CMS\Dbal\Database\SqlCompilers\Mysql
+	 */
+	protected function getSqlCompiler() {
+		if ((string)$this->databaseConnection->handlerCfg[$this->databaseConnection->lastHandlerKey]['type'] === 'native') {
+			return $this->nativeSqlCompiler;
+		} else {
+			return $this->sqlCompiler;
 		}
-		return $output;
 	}
 
+	/*************************
+	 *
+	 * Debugging
+	 *
+	 *************************/
 	/**
 	 * Performs the ultimate test of the parser: Direct a SQL query in; You will get it back (through the parsed and re-compiled) if no problems, otherwise the script will print the error and exit
 	 *
@@ -806,4 +1758,59 @@ class SqlParser extends \TYPO3\CMS\Core\Database\SqlParser {
 		}
 	}
 
+	/**
+	 * Check parsability of input SQL part string; Will parse and re-compile after which it is compared
+	 *
+	 * @param string $part Part definition of string; "SELECT" = fieldlist (also ORDER BY and GROUP BY), "FROM" = table list, "WHERE" = Where clause.
+	 * @param string $str SQL string to verify parsability of
+	 * @return mixed Returns array with string 1 and 2 if error, otherwise FALSE
+	 */
+	public function debug_parseSQLpart($part, $str) {
+		$retVal = FALSE;
+		switch ($part) {
+			case 'SELECT':
+				$retVal = $this->debug_parseSQLpartCompare($str, $this->compileFieldList($this->parseFieldList($str)));
+				break;
+			case 'FROM':
+				$retVal = $this->debug_parseSQLpartCompare($str, $this->getSqlCompiler()->compileFromTables($this->parseFromTables($str)));
+				break;
+			case 'WHERE':
+				$retVal = $this->debug_parseSQLpartCompare($str, $this->getSqlCompiler()->compileWhereClause($this->parseWhereClause($str)));
+				break;
+		}
+		return $retVal;
+	}
+
+	/**
+	 * Compare two query strings by stripping away whitespace.
+	 *
+	 * @param string $str SQL String 1
+	 * @param string $newStr SQL string 2
+	 * @param bool $caseInsensitive If TRUE, the strings are compared insensitive to case
+	 * @return mixed Returns array with string 1 and 2 if error, otherwise FALSE
+	 */
+	public function debug_parseSQLpartCompare($str, $newStr, $caseInsensitive = FALSE) {
+		if ($caseInsensitive) {
+			$str1 = strtoupper($str);
+			$str2 = strtoupper($newStr);
+		} else {
+			$str1 = $str;
+			$str2 = $newStr;
+		}
+
+		// Fixing escaped chars:
+		$search = array(NUL, LF, CR, SUB);
+		$replace = array("\x00", "\x0a", "\x0d", "\x1a");
+		$str1 = str_replace($search, $replace, $str1);
+		$str2 = str_replace($search, $replace, $str2);
+
+		$search = self::$interQueryWhitespaces;
+		if (str_replace($search, '', $this->trimSQL($str1)) !== str_replace($search, '', $this->trimSQL($str2))) {
+			return array(
+				str_replace($search, ' ', $str),
+				str_replace($search, ' ', $newStr),
+			);
+		}
+	}
+
 }
diff --git a/typo3/sysext/dbal/Tests/Unit/Database/AbstractTestCase.php b/typo3/sysext/dbal/Tests/Unit/Database/AbstractTestCase.php
index c6f0969e084fd6f7dcd44eb410a65874b24bde78..fc09dd168164cd60440eeaa63fb0b5837bf66ab5 100644
--- a/typo3/sysext/dbal/Tests/Unit/Database/AbstractTestCase.php
+++ b/typo3/sysext/dbal/Tests/Unit/Database/AbstractTestCase.php
@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Dbal\Tests\Unit\Database;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
 require_once __DIR__ . '/../../../../adodb/adodb/adodb.inc.php';
 require_once __DIR__ . '/../../../../adodb/adodb/drivers/adodb-mssql.inc.php';
 require_once __DIR__ . '/../../../../adodb/adodb/drivers/adodb-oci8.inc.php';
@@ -45,6 +47,8 @@ abstract class AbstractTestCase extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 		// Inject SqlParser - Its logic is tested with the tests, too.
 		$sqlParser = $this->getAccessibleMock(\TYPO3\CMS\Dbal\Database\SqlParser::class, array('dummy'), array(), '', FALSE);
 		$sqlParser->_set('databaseConnection', $subject);
+		$sqlParser->_set('sqlCompiler', GeneralUtility::makeInstance(\TYPO3\CMS\Dbal\Database\SqlCompilers\Adodb::class, $subject));
+		$sqlParser->_set('nativeSqlCompiler', GeneralUtility::makeInstance(\TYPO3\CMS\Dbal\Database\SqlCompilers\Mysql::class, $subject));
 		$subject->SQLparser = $sqlParser;
 
 		// Mock away schema migration service from install tool
@@ -81,4 +85,4 @@ abstract class AbstractTestCase extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 		return trim($sql);
 	}
 
-}
\ No newline at end of file
+}
diff --git a/typo3/sysext/dbal/Tests/Unit/Database/DatabaseConnectionOracleTest.php b/typo3/sysext/dbal/Tests/Unit/Database/DatabaseConnectionOracleTest.php
index 5c41c82e40068e2579ba9ab03791b044e4bef362..73f7cafce9df433b260188a2f374700536d82ff4 100644
--- a/typo3/sysext/dbal/Tests/Unit/Database/DatabaseConnectionOracleTest.php
+++ b/typo3/sysext/dbal/Tests/Unit/Database/DatabaseConnectionOracleTest.php
@@ -131,7 +131,7 @@ class DatabaseConnectionOracleTest extends AbstractTestCase {
 		$parseString .= 'VALUES (\'1\', \'0\', \'2\', \'0\', \'Africa\');';
 		$components = $this->subject->SQLparser->_callRef('parseINSERT', $parseString);
 		$this->assertTrue(is_array($components), $components);
-		$insert = $this->subject->SQLparser->_callRef('compileINSERT', $components);
+		$insert = $this->subject->SQLparser->compileSQL($components);
 		$expected = array(
 			'uid' => '1',
 			'pid' => '0',
@@ -151,7 +151,7 @@ class DatabaseConnectionOracleTest extends AbstractTestCase {
 		$parseString = 'INSERT INTO static_territories VALUES (\'1\', \'0\', \'2\', \'0\', \'Africa\'),(\'2\', \'0\', \'9\', \'0\', \'Oceania\'),' . '(\'3\', \'0\', \'19\', \'0\', \'Americas\'),(\'4\', \'0\', \'142\', \'0\', \'Asia\');';
 		$components = $this->subject->SQLparser->_callRef('parseINSERT', $parseString);
 		$this->assertTrue(is_array($components), $components);
-		$insert = $this->subject->SQLparser->_callRef('compileINSERT', $components);
+		$insert = $this->subject->SQLparser->compileSQL($components);
 		$insertCount = count($insert);
 		$this->assertEquals(4, $insertCount);
 		for ($i = 0; $i < $insertCount; $i++) {
@@ -643,7 +643,7 @@ class DatabaseConnectionOracleTest extends AbstractTestCase {
 		';
 		$components = $this->subject->SQLparser->_callRef('parseCREATETABLE', $parseString);
 		$this->assertTrue(is_array($components), 'Not an array: ' . $components);
-		$sqlCommands = $this->subject->SQLparser->_call('compileCREATETABLE', $components);
+		$sqlCommands = $this->subject->SQLparser->compileSQL($components);
 		$this->assertTrue(is_array($sqlCommands), 'Not an array: ' . $sqlCommands);
 		$this->assertEquals(6, count($sqlCommands));
 		$expected = $this->cleanSql('
@@ -683,7 +683,7 @@ class DatabaseConnectionOracleTest extends AbstractTestCase {
 		';
 		$components = $this->subject->SQLparser->_callRef('parseCREATETABLE', $parseString);
 		$this->assertTrue(is_array($components), 'Not an array: ' . $components);
-		$sqlCommands = $this->subject->SQLparser->_call('compileCREATETABLE', $components);
+		$sqlCommands = $this->subject->SQLparser->compileSQL($components);
 		$this->assertTrue(is_array($sqlCommands), 'Not an array: ' . $sqlCommands);
 		$this->assertEquals(4, count($sqlCommands));
 		$expected = $this->cleanSql('
diff --git a/typo3/sysext/dbal/Tests/Unit/Database/DatabaseConnectionPostgresqlTest.php b/typo3/sysext/dbal/Tests/Unit/Database/DatabaseConnectionPostgresqlTest.php
index 638f1f306f81a28d0ab88358841867d30ccd0818..7de4814ae92c4dc4bed8eeb6d1c4b9fa3b90db7e 100644
--- a/typo3/sysext/dbal/Tests/Unit/Database/DatabaseConnectionPostgresqlTest.php
+++ b/typo3/sysext/dbal/Tests/Unit/Database/DatabaseConnectionPostgresqlTest.php
@@ -153,7 +153,7 @@ class DatabaseConnectionPostgresqlTest extends AbstractTestCase {
 		$components = $this->subject->SQLparser->_callRef('parseALTERTABLE', $parseString);
 		$this->assertInternalType('array', $components);
 
-		$result = $this->subject->SQLparser->_callRef('compileALTERTABLE', $components);
+		$result = $this->subject->SQLparser->compileSQL($components);
 		$expected = array('CREATE INDEX "dd81ee97_parent" ON "sys_collection" ("pid", "deleted")');
 		$this->assertSame($expected, $this->cleanSql($result));
 	}
@@ -167,7 +167,7 @@ class DatabaseConnectionPostgresqlTest extends AbstractTestCase {
 		$components = $this->subject->SQLparser->_callRef('parseALTERTABLE', $parseString);
 		$this->assertInternalType('array', $components);
 
-		$result = $this->subject->SQLparser->_callRef('compileALTERTABLE', $components);
+		$result = $this->subject->SQLparser->compileSQL($components);
 		$expected = array('DROP INDEX "dd81ee97_parent"');
 		$this->assertSame($expected, $this->cleanSql($result));
 	}
diff --git a/typo3/sysext/dbal/Tests/Unit/Database/SqlParserTest.php b/typo3/sysext/dbal/Tests/Unit/Database/SqlParserTest.php
index f5b0cd7c3414f7057c08f59986a32bcaac64e0f2..6e9c4d5a6a45b4edd88ccfdbe083cd632df40045 100644
--- a/typo3/sysext/dbal/Tests/Unit/Database/SqlParserTest.php
+++ b/typo3/sysext/dbal/Tests/Unit/Database/SqlParserTest.php
@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Dbal\Tests\Unit\Database;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
 /**
  * Test case
  */
@@ -33,10 +35,473 @@ class SqlParserTest extends AbstractTestCase {
 		$mockDatabaseConnection = $this->getMock(\TYPO3\CMS\Dbal\Database\DatabaseConnection::class, array(), array(), '', FALSE);
 		$mockDatabaseConnection->lastHandlerKey = '_DEFAULT';
 		$subject->_set('databaseConnection', $mockDatabaseConnection);
+		$subject->_set('sqlCompiler', GeneralUtility::makeInstance(\TYPO3\CMS\Dbal\Database\SqlCompilers\Adodb::class, $mockDatabaseConnection));
+		$subject->_set('nativeSqlCompiler', GeneralUtility::makeInstance(\TYPO3\CMS\Dbal\Database\SqlCompilers\Mysql::class, $mockDatabaseConnection));
 
 		$this->subject = $subject;
 	}
 
+	/**
+	 * Regression test
+	 *
+	 * @test
+	 */
+	public function compileWhereClauseDoesNotDropClauses() {
+		$clauses = array(
+			0 => array(
+				'modifier' => '',
+				'table' => 'pages',
+				'field' => 'fe_group',
+				'calc' => '',
+				'comparator' => '=',
+				'value' => array(
+					0 => '',
+					1 => '\''
+				)
+			),
+			1 => array(
+				'operator' => 'OR',
+				'modifier' => '',
+				'func' => array(
+					'type' => 'IFNULL',
+					'default' => array(
+						0 => '1',
+						1 => '\''
+					),
+					'table' => 'pages',
+					'field' => 'fe_group'
+				)
+			),
+			2 => array(
+				'operator' => 'OR',
+				'modifier' => '',
+				'table' => 'pages',
+				'field' => 'fe_group',
+				'calc' => '',
+				'comparator' => '=',
+				'value' => array(
+					0 => '0',
+					1 => '\''
+				)
+			),
+			3 => array(
+				'operator' => 'OR',
+				'modifier' => '',
+				'func' => array(
+					'type' => 'FIND_IN_SET',
+					'str' => array(
+						0 => '0',
+						1 => '\''
+					),
+					'table' => 'pages',
+					'field' => 'fe_group'
+				),
+				'comparator' => ''
+			),
+			4 => array(
+				'operator' => 'OR',
+				'modifier' => '',
+				'func' => array(
+					'type' => 'FIND_IN_SET',
+					'str' => array(
+						0 => '-1',
+						1 => '\''
+					),
+					'table' => 'pages',
+					'field' => 'fe_group'
+				),
+				'comparator' => ''
+			),
+			5 => array(
+				'operator' => 'OR',
+				'modifier' => '',
+				'func' => array(
+					'type' => 'CAST',
+					'table' => 'pages',
+					'field' => 'fe_group',
+					'datatype' => 'CHAR'
+				),
+				'comparator' => '=',
+				'value' => array(
+					0 => '',
+					1 => '\''
+				)
+			)
+		);
+		$output = $this->subject->compileWhereClause($clauses);
+		$parts = explode(' OR ', $output);
+		$this->assertSame(count($clauses), count($parts));
+		$this->assertContains('IFNULL', $output);
+	}
+
+	/**
+	 * Data provider for trimSqlReallyTrimsAllWhitespace
+	 *
+	 * @see trimSqlReallyTrimsAllWhitespace
+	 */
+	public function trimSqlReallyTrimsAllWhitespaceDataProvider() {
+		return array(
+			'Nothing to trim' => array('SELECT * FROM test WHERE 1=1;', 'SELECT * FROM test WHERE 1=1 '),
+			'Space after ;' => array('SELECT * FROM test WHERE 1=1; ', 'SELECT * FROM test WHERE 1=1 '),
+			'Space before ;' => array('SELECT * FROM test WHERE 1=1 ;', 'SELECT * FROM test WHERE 1=1 '),
+			'Space before and after ;' => array('SELECT * FROM test WHERE 1=1 ; ', 'SELECT * FROM test WHERE 1=1 '),
+			'Linefeed after ;' => array('SELECT * FROM test WHERE 1=1' . LF . ';', 'SELECT * FROM test WHERE 1=1 '),
+			'Linefeed before ;' => array('SELECT * FROM test WHERE 1=1;' . LF, 'SELECT * FROM test WHERE 1=1 '),
+			'Linefeed before and after ;' => array('SELECT * FROM test WHERE 1=1' . LF . ';' . LF, 'SELECT * FROM test WHERE 1=1 '),
+			'Tab after ;' => array('SELECT * FROM test WHERE 1=1' . TAB . ';', 'SELECT * FROM test WHERE 1=1 '),
+			'Tab before ;' => array('SELECT * FROM test WHERE 1=1;' . TAB, 'SELECT * FROM test WHERE 1=1 '),
+			'Tab before and after ;' => array('SELECT * FROM test WHERE 1=1' . TAB . ';' . TAB, 'SELECT * FROM test WHERE 1=1 '),
+		);
+	}
+
+	/**
+	 * @test
+	 * @dataProvider trimSqlReallyTrimsAllWhitespaceDataProvider
+	 * @param string $sql The SQL to trim
+	 * @param string $expected The expected trimmed SQL with single space at the end
+	 */
+	public function trimSqlReallyTrimsAllWhitespace($sql, $expected) {
+		$result = $this->subject->_call('trimSQL', $sql);
+		$this->assertSame($expected, $result);
+	}
+
+	/**
+	 * Data provider for getValueReturnsCorrectValues
+	 *
+	 * @see getValueReturnsCorrectValues
+	 */
+	public function getValueReturnsCorrectValuesDataProvider() {
+		return array(
+			// description => array($parseString, $comparator, $mode, $expected)
+			'key definition without length' => array('(pid,input_1), ', '_LIST', 'INDEX', array('pid', 'input_1')),
+			'key definition with length' => array('(pid,input_1(30)), ', '_LIST', 'INDEX', array('pid', 'input_1(30)')),
+			'key definition without length (no mode)' => array('(pid,input_1), ', '_LIST', '', array('pid', 'input_1')),
+			'key definition with length (no mode)' => array('(pid,input_1(30)), ', '_LIST', '', array('pid', 'input_1(30)')),
+			'test1' => array('input_1 varchar(255) DEFAULT \'\' NOT NULL,', '', '', array('input_1')),
+			'test2' => array('varchar(255) DEFAULT \'\' NOT NULL,', '', '', array('varchar(255)')),
+			'test3' => array('DEFAULT \'\' NOT NULL,', '', '', array('DEFAULT')),
+			'test4' => array('\'\' NOT NULL,', '', '', array('', '\'')),
+			'test5' => array('NOT NULL,', '', '', array('NOT')),
+			'test6' => array('NULL,', '', '', array('NULL')),
+			'getValueOrParameter' => array('NULL,', '', '', array('NULL')),
+		);
+	}
+
+	/**
+	 * @test
+	 * @dataProvider getValueReturnsCorrectValuesDataProvider
+	 * @param string $parseString the string to parse
+	 * @param string $comparator The comparator used before. If "NOT IN" or "IN" then the value is expected to be a list of values. Otherwise just an integer (un-quoted) or string (quoted)
+	 * @param string $mode The mode, eg. "INDEX
+	 * @param string $expected
+	 */
+	public function getValueReturnsCorrectValues($parseString, $comparator, $mode, $expected) {
+		$result = $this->subject->_callRef('getValue', $parseString, $comparator, $mode);
+		$this->assertSame($expected, $result);
+	}
+
+	/**
+	 * Data provider for parseSQL
+	 *
+	 * @see parseSQL
+	 */
+	public function parseSQLDataProvider() {
+		$testSql = array();
+		$testSql[] = 'CREATE TABLE tx_demo (';
+		$testSql[] = '	uid int(11) NOT NULL auto_increment,';
+		$testSql[] = '	pid int(11) DEFAULT \'0\' NOT NULL,';
+
+		$testSql[] = '	tstamp int(11) unsigned DEFAULT \'0\' NOT NULL,';
+		$testSql[] = '	crdate int(11) unsigned DEFAULT \'0\' NOT NULL,';
+		$testSql[] = '	cruser_id int(11) unsigned DEFAULT \'0\' NOT NULL,';
+		$testSql[] = '	deleted tinyint(4) unsigned DEFAULT \'0\' NOT NULL,';
+		$testSql[] = '	hidden tinyint(4) unsigned DEFAULT \'0\' NOT NULL,';
+		$testSql[] = '	starttime int(11) unsigned DEFAULT \'0\' NOT NULL,';
+		$testSql[] = '	endtime int(11) unsigned DEFAULT \'0\' NOT NULL,';
+
+		$testSql[] = '	input_1 varchar(255) DEFAULT \'\' NOT NULL,';
+		$testSql[] = '	input_2 varchar(255) DEFAULT \'\' NOT NULL,';
+		$testSql[] = '	select_child int(11) unsigned DEFAULT \'0\' NOT NULL,';
+
+		$testSql[] = '	PRIMARY KEY (uid),';
+		$testSql[] = '	KEY parent (pid,input_1),';
+		$testSql[] = '	KEY bar (tstamp,input_1(200),input_2(100),endtime)';
+		$testSql[] = ');';
+		$testSql = implode("\n", $testSql);
+		$expected = array(
+			'type' => 'CREATETABLE',
+			'TABLE' => 'tx_demo',
+			'FIELDS' => array(
+				'uid' => array(
+					'definition' => array(
+						'fieldType' => 'int',
+						'value' => '11',
+						'featureIndex' => array(
+							'NOTNULL' => array(
+								'keyword' => 'NOT NULL'
+							),
+							'AUTO_INCREMENT' => array(
+								'keyword' => 'auto_increment'
+							)
+						)
+					)
+				),
+				'pid' => array(
+					'definition' => array(
+						'fieldType' => 'int',
+						'value' => '11',
+						'featureIndex' => array(
+							'DEFAULT' => array(
+								'keyword' => 'DEFAULT',
+								'value' => array(
+									0 => '0',
+									1 => '\'',
+								)
+							),
+							'NOTNULL' => array(
+								'keyword' => 'NOT NULL'
+							)
+						)
+					)
+				),
+				'tstamp' => array(
+					'definition' => array(
+						'fieldType' => 'int',
+						'value' => '11',
+						'featureIndex' => array(
+							'UNSIGNED' => array(
+								'keyword' => 'unsigned'
+							),
+							'DEFAULT' => array(
+								'keyword' => 'DEFAULT',
+								'value' => array(
+									0 => '0',
+									1 => '\''
+								)
+							),
+							'NOTNULL' => array(
+								'keyword' => 'NOT NULL'
+							)
+						)
+					)
+				),
+				'crdate' => array(
+					'definition' => array(
+						'fieldType' => 'int',
+						'value' => '11',
+						'featureIndex' => array(
+							'UNSIGNED' => array(
+								'keyword' => 'unsigned'
+							),
+							'DEFAULT' => array(
+								'keyword' => 'DEFAULT',
+								'value' => array(
+									0 => '0',
+									1 => '\''
+								)
+							),
+							'NOTNULL' => array(
+								'keyword' => 'NOT NULL'
+							)
+						)
+					)
+				),
+				'cruser_id' => array(
+					'definition' => array(
+						'fieldType' => 'int',
+						'value' => '11',
+						'featureIndex' => array(
+							'UNSIGNED' => array(
+								'keyword' => 'unsigned'
+							),
+							'DEFAULT' => array(
+								'keyword' => 'DEFAULT',
+								'value' => array(
+									0 => '0',
+									1 => '\'',
+								)
+							),
+							'NOTNULL' => array(
+								'keyword' => 'NOT NULL'
+							)
+						)
+					)
+				),
+				'deleted' => array(
+					'definition' => array(
+						'fieldType' => 'tinyint',
+						'value' => '4',
+						'featureIndex' => array(
+							'UNSIGNED' => array(
+								'keyword' => 'unsigned'
+							),
+							'DEFAULT' => array(
+								'keyword' => 'DEFAULT',
+								'value' => array(
+									0 => '0',
+									1 => '\''
+								)
+							),
+							'NOTNULL' => array(
+								'keyword' => 'NOT NULL'
+							)
+						)
+					)
+				),
+				'hidden' => array(
+					'definition' => array(
+						'fieldType' => 'tinyint',
+						'value' => '4',
+						'featureIndex' => array(
+							'UNSIGNED' => array(
+								'keyword' => 'unsigned'
+							),
+							'DEFAULT' => array(
+								'keyword' => 'DEFAULT',
+								'value' => array(
+									0 => '0',
+									1 => '\''
+								)
+							),
+							'NOTNULL' => array(
+								'keyword' => 'NOT NULL'
+							)
+						)
+					)
+				),
+				'starttime' => array(
+					'definition' => array(
+						'fieldType' => 'int',
+						'value' => '11',
+						'featureIndex' => array(
+							'UNSIGNED' => array(
+								'keyword' => 'unsigned'
+							),
+							'DEFAULT' => array(
+								'keyword' => 'DEFAULT',
+								'value' => array(
+									0 => '0',
+									1 => '\''
+								)
+							),
+							'NOTNULL' => array(
+								'keyword' => 'NOT NULL'
+							)
+						)
+					)
+				),
+				'endtime' => array(
+					'definition' => array(
+						'fieldType' => 'int',
+						'value' => '11',
+						'featureIndex' => array(
+							'UNSIGNED' => array(
+								'keyword' => 'unsigned'
+							),
+							'DEFAULT' => array(
+								'keyword' => 'DEFAULT',
+								'value' => array(
+									0 => '0',
+									1 => '\'',
+								)
+							),
+							'NOTNULL' => array(
+								'keyword' => 'NOT NULL'
+							)
+						)
+					)
+				),
+				'input_1' => array(
+					'definition' => array(
+						'fieldType' => 'varchar',
+						'value' => '255',
+						'featureIndex' => array(
+							'DEFAULT' => array(
+								'keyword' => 'DEFAULT',
+								'value' => array(
+									0 => '',
+									1 => '\'',
+								)
+							),
+							'NOTNULL' => array(
+								'keyword' => 'NOT NULL'
+							)
+						)
+					)
+				),
+				'input_2' => array(
+					'definition' => array(
+						'fieldType' => 'varchar',
+						'value' => '255',
+						'featureIndex' => array(
+							'DEFAULT' => array(
+								'keyword' => 'DEFAULT',
+								'value' => array(
+									0 => '',
+									1 => '\'',
+								)
+							),
+							'NOTNULL' => array(
+								'keyword' => 'NOT NULL'
+							)
+						)
+					)
+				),
+				'select_child' => array(
+					'definition' => array(
+						'fieldType' => 'int',
+						'value' => '11',
+						'featureIndex' => array(
+							'UNSIGNED' => array(
+								'keyword' => 'unsigned'
+							),
+							'DEFAULT' => array(
+								'keyword' => 'DEFAULT',
+								'value' => array(
+									0 => '0',
+									1 => '\''
+								)
+							),
+							'NOTNULL' => array(
+								'keyword' => 'NOT NULL'
+							)
+						)
+					)
+				)
+			),
+			'KEYS' => array(
+				'PRIMARYKEY' => array(
+					0 => 'uid'
+				),
+				'parent' => array(
+					0 => 'pid',
+					1 => 'input_1',
+				),
+				'bar' => array(
+					0 => 'tstamp',
+					1 => 'input_1(200)',
+					2 => 'input_2(100)',
+					3 => 'endtime',
+				)
+			)
+		);
+
+		return array(
+			'test1' => array($testSql, $expected)
+		);
+	}
+
+	/**
+	 * @test
+	 * @dataProvider parseSQLDataProvider
+	 * @param string $sql The SQL to trim
+	 * @param array $expected The expected trimmed SQL with single space at the end
+	 */
+	public function parseSQL($sql, $expected) {
+		$result = $this->subject->_callRef('parseSQL', $sql);
+		$this->assertSame($expected, $result);
+	}
+
 	/**
 	 * @test
 	 */
@@ -165,7 +630,7 @@ class SqlParserTest extends AbstractTestCase {
 		$components = $this->subject->_callRef('parseINSERT', $parseString);
 		$this->assertInternalType('array', $components);
 
-		$result = $this->subject->_callRef('compileINSERT', $components);
+		$result = $this->subject->compileSQL($components);
 		$expected = 'INSERT INTO static_country_zones VALUES (\'483\',\'0\',\'NL\',\'NLD\',\'528\',\'DR\',\'Drenthe\',\'\')';
 		$this->assertEquals($expected, $this->cleanSql($result));
 	}
@@ -178,7 +643,7 @@ class SqlParserTest extends AbstractTestCase {
 		$components = $this->subject->_callRef('parseINSERT', $parseString);
 		$this->assertInternalType('array', $components);
 
-		$result = $this->subject->_callRef('compileINSERT', $components);
+		$result = $this->subject->compileSQL($components);
 		$expected = 'INSERT INTO static_country_zones VALUES (\'483\',\'0\',\'NL\',\'NLD\',\'528\',\'DR\',\'Drenthe\',\'\')';
 		$this->assertEquals($expected, $this->cleanSql($result));
 	}
@@ -192,7 +657,7 @@ class SqlParserTest extends AbstractTestCase {
 		$components = $this->subject->_callRef('parseINSERT', $parseString);
 		$this->assertInternalType('array', $components);
 
-		$result = $this->subject->_callRef('compileINSERT', $components);
+		$result = $this->subject->compileSQL($components);
 		$expected = 'INSERT INTO static_territories (uid,pid,tr_iso_nr,tr_parent_iso_nr,tr_name_en) ';
 		$expected .= 'VALUES (\'1\',\'0\',\'2\',\'0\',\'Africa\')';
 		$this->assertEquals($expected, $this->cleanSql($result));
@@ -206,7 +671,7 @@ class SqlParserTest extends AbstractTestCase {
 		$components = $this->subject->_callRef('parseINSERT', $parseString);
 		$this->assertInternalType('array', $components);
 
-		$result = $this->subject->_callRef('compileINSERT', $components);
+		$result = $this->subject->compileSQL($components);
 		$expected = 'INSERT INTO static_territories VALUES (\'1\',\'0\',\'2\',\'0\',\'Africa\'),(\'2\',\'0\',\'9\',\'0\',\'Oceania\'),(\'3\',\'0\',\'19\',\'0\',\'Americas\'),(\'4\',\'0\',\'142\',\'0\',\'Asia\')';
 		$this->assertEquals($expected, $this->cleanSql($result));
 	}
@@ -220,7 +685,7 @@ class SqlParserTest extends AbstractTestCase {
 		$components = $this->subject->_callRef('parseINSERT', $parseString);
 		$this->assertInternalType('array', $components);
 
-		$result = $this->subject->_callRef('compileINSERT', $components);
+		$result = $this->subject->compileSQL($components);
 		$expected = 'INSERT INTO static_territories (uid,pid,tr_iso_nr,tr_parent_iso_nr,tr_name_en) ';
 		$expected .= 'VALUES (\'1\',\'0\',\'2\',\'0\',\'Africa\'),(\'2\',\'0\',\'9\',\'0\',\'Oceania\')';
 		$this->assertEquals($expected, $this->cleanSql($result));
@@ -264,7 +729,7 @@ class SqlParserTest extends AbstractTestCase {
 		$components = $this->subject->_callRef('parseSELECT', $parseString);
 		$this->assertInternalType('array', $components);
 
-		$result = $this->subject->_callRef('compileSELECT', $components);
+		$result = $this->subject->compileSQL($components);
 		$expected = 'SELECT * FROM tx_irfaq_q_cat_mm WHERE IFNULL(tx_irfaq_q_cat_mm.uid_foreign, 0) = 1';
 		$this->assertEquals($expected, $this->cleanSql($result));
 	}
@@ -289,7 +754,7 @@ class SqlParserTest extends AbstractTestCase {
 		$components = $this->subject->_callRef('parseSELECT', $parseString);
 		$this->assertInternalType('array', $components);
 
-		$result = $this->subject->_callRef('compileSELECT', $components);
+		$result = $this->subject->compileSQL($components);
 		$expected = 'SELECT * FROM sys_category WHERE CAST(parent AS CHAR) != \'\'';
 		$this->assertEquals($expected, $this->cleanSql($result));
 	}
@@ -303,7 +768,7 @@ class SqlParserTest extends AbstractTestCase {
 		$components = $this->subject->_callRef('parseALTERTABLE', $parseString);
 		$this->assertInternalType('array', $components);
 
-		$result = $this->subject->_callRef('compileALTERTABLE', $components);
+		$result = $this->subject->compileSQL($components);
 		$expected = 'ALTER TABLE tx_realurl_pathcache ENGINE = InnoDB';
 		$this->assertEquals($expected, $this->cleanSql($result));
 	}
@@ -317,7 +782,7 @@ class SqlParserTest extends AbstractTestCase {
 		$components = $this->subject->_callRef('parseALTERTABLE', $parseString);
 		$this->assertInternalType('array', $components);
 
-		$result = $this->subject->_callRef('compileALTERTABLE', $components);
+		$result = $this->subject->compileSQL($components);
 		$expected = 'ALTER TABLE index_phash DEFAULT CHARACTER SET utf8';
 		$this->assertEquals($expected, $this->cleanSql($result));
 	}
@@ -331,7 +796,7 @@ class SqlParserTest extends AbstractTestCase {
 		$components = $this->subject->_callRef('parseALTERTABLE', $parseString);
 		$this->assertInternalType('array', $components);
 
-		$result = $this->subject->_callRef('compileALTERTABLE', $components);
+		$result = $this->subject->compileSQL($components);
 		$expected = 'ALTER TABLE sys_collection ADD KEY parent (pid,deleted)';
 		$this->assertSame($expected, $this->cleanSql($result));
 	}
@@ -345,7 +810,7 @@ class SqlParserTest extends AbstractTestCase {
 		$components = $this->subject->_callRef('parseALTERTABLE', $parseString);
 		$this->assertInternalType('array', $components);
 
-		$result = $this->subject->_callRef('compileALTERTABLE', $components);
+		$result = $this->subject->compileSQL($components);
 		$expected = 'ALTER TABLE sys_collection DROP KEY parent';
 		$this->assertSame($expected, $this->cleanSql($result));
 	}
@@ -359,7 +824,7 @@ class SqlParserTest extends AbstractTestCase {
 		$components = $this->subject->_callRef('parseSELECT', $parseString);
 		$this->assertInternalType('array', $components);
 
-		$result = $this->subject->_callRef('compileSELECT', $components);
+		$result = $this->subject->compileSQL($components);
 		$expected = 'SELECT * FROM fe_users WHERE FIND_IN_SET(10, usergroup)';
 		$this->assertEquals($expected, $this->cleanSql($result));
 	}
@@ -751,7 +1216,7 @@ class SqlParserTest extends AbstractTestCase {
 		$components = $this->subject->_callRef('parseSELECT', $sql);
 		$components['parameters'][':pageId'][0] = $pageId;
 
-		$result = $this->subject->_callRef('compileSELECT', $components);
+		$result = $this->subject->compileSQL($components);
 		$expected = 'SELECT * FROM pages WHERE uid = 12 OR uid IN (SELECT uid FROM pages WHERE pid = 12)';
 		$this->assertEquals($expected, $this->cleanSql($result));
 	}
@@ -766,7 +1231,7 @@ class SqlParserTest extends AbstractTestCase {
 		$components = $this->subject->_callRef('parseSELECT', $sql);
 		$components['parameters'][':pid'][0] = $pid;
 
-		$result = $this->subject->_callRef('compileSELECT', $components);
+		$result = $this->subject->compileSQL($components);
 		$expected = 'SELECT * FROM pages WHERE pid = ' . $pid . ' AND title NOT LIKE \':pid\'';
 		$this->assertEquals($expected, $this->cleanSql($result));
 	}
@@ -784,7 +1249,7 @@ class SqlParserTest extends AbstractTestCase {
 			$components['parameters']['?'][$i][0] = $parameterValues[$i];
 		}
 
-		$result = $this->subject->_callRef('compileSELECT', $components);
+		$result = $this->subject->compileSQL($components);
 		$expected = 'SELECT * FROM pages WHERE pid = 12 AND timestamp < 1281782690 AND title != \'How to test?\'';
 		$this->assertEquals($expected, $this->cleanSql($result));
 	}
diff --git a/typo3/sysext/dbal/ext_localconf.php b/typo3/sysext/dbal/ext_localconf.php
index 60aaa507abba8f6c9ee8e344b8dc10f00e0157bf..31f004d008a1b3478a20024c20fa46d285edef84 100644
--- a/typo3/sysext/dbal/ext_localconf.php
+++ b/typo3/sysext/dbal/ext_localconf.php
@@ -2,7 +2,6 @@
 defined('TYPO3_MODE') or die();
 
 $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][\TYPO3\CMS\Core\Database\DatabaseConnection::class] = array('className' => \TYPO3\CMS\Dbal\Database\DatabaseConnection::class);
-$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][\TYPO3\CMS\Core\Database\SqlParser::class] = array('className' => \TYPO3\CMS\Dbal\Database\SqlParser::class);
 $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][\TYPO3\CMS\Recordlist\RecordList\DatabaseRecordList::class] = array('className' => \TYPO3\CMS\Dbal\RecordList\DatabaseRecordList::class);
 
 // Register caches if not already done in localconf.php or a previously loaded extension.