Fixed bug #6196: IFNULL operator cannot be parsed
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_sqlparser.php
old mode 100755 (executable)
new mode 100644 (file)
index df07db5..e9561c0
@@ -2,7 +2,7 @@
 /***************************************************************
 *  Copyright notice
 *
-*  (c) 2004 Kasper Skaarhoj (kasperYYYY@typo3.com)
+*  (c) 2004-2010 Kasper Skaarhoj (kasperYYYY@typo3.com)
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
  *
  *
  *
- *  106: class t3lib_sqlparser
+ *  107: class t3lib_sqlparser
  *
  *              SECTION: SQL Parsing, full queries
- *  128:     function parseSQL($parseString)
- *  188:     function parseSELECT($parseString)
- *  257:     function parseUPDATE($parseString)
- *  311:     function parseINSERT($parseString)
- *  371:     function parseDELETE($parseString)
- *  409:     function parseEXPLAIN($parseString)
- *  431:     function parseCREATETABLE($parseString)
- *  503:     function parseALTERTABLE($parseString)
- *  572:     function parseDROPTABLE($parseString)
+ *  129:     function parseSQL($parseString)
+ *  192:     function parseSELECT($parseString)
+ *  261:     function parseUPDATE($parseString)
+ *  315:     function parseINSERT($parseString)
+ *  375:     function parseDELETE($parseString)
+ *  413:     function parseEXPLAIN($parseString)
+ *  435:     function parseCREATETABLE($parseString)
+ *  514:     function parseALTERTABLE($parseString)
+ *  583:     function parseDROPTABLE($parseString)
+ *  616:     function parseCREATEDATABASE($parseString)
  *
  *              SECTION: SQL Parsing, helper functions for parts of queries
- *  631:     function parseFieldList(&$parseString, $stopRegex='')
- *  749:     function parseFromTables(&$parseString, $stopRegex='')
- *  816:     function parseWhereClause(&$parseString, $stopRegex='')
- *  924:     function parseFieldDef(&$parseString, $stopRegex='')
+ *  670:     function parseFieldList(&$parseString, $stopRegex='')
+ *  791:     function parseFromTables(&$parseString, $stopRegex='')
+ *  882:     function parseWhereClause(&$parseString, $stopRegex='')
+ *  990:     function parseFieldDef(&$parseString, $stopRegex='')
  *
  *              SECTION: Parsing: Helper functions
- *  985:     function nextPart(&$parseString,$regex,$trimAll=FALSE)
- *  999:     function getValue(&$parseString,$comparator='')
- * 1054:     function getValueInQuotes(&$parseString,$quote)
- * 1079:     function parseStripslashes($str)
- * 1093:     function compileAddslashes($str)
- * 1107:     function parseError($msg,$restQuery)
- * 1121:     function trimSQL($str)
+ * 1053:     function nextPart(&$parseString,$regex,$trimAll=FALSE)
+ * 1068:     function getValue(&$parseString,$comparator='')
+ * 1127:     function getValueInQuotes(&$parseString,$quote)
+ * 1153:     function parseStripslashes($str)
+ * 1167:     function compileAddslashes($str)
+ * 1182:     function parseError($msg,$restQuery)
+ * 1196:     function trimSQL($str)
  *
  *              SECTION: Compiling queries
- * 1149:     function compileSQL($components)
- * 1187:     function compileSELECT($components)
- * 1218:     function compileUPDATE($components)
- * 1246:     function compileINSERT($components)
- * 1286:     function compileDELETE($components)
- * 1306:     function compileCREATETABLE($components)
- * 1337:     function compileALTERTABLE($components)
+ * 1225:     function compileSQL($components)
+ * 1263:     function compileSELECT($components)
+ * 1294:     function compileUPDATE($components)
+ * 1322:     function compileINSERT($components)
+ * 1362:     function compileDELETE($components)
+ * 1382:     function compileCREATETABLE($components)
+ * 1415:     function compileALTERTABLE($components)
  *
  *              SECTION: Compiling queries, helper functions for parts of queries
- * 1390:     function compileFieldList($selectFields)
- * 1432:     function compileFromTables($tablesArray)
- * 1468:     function compileWhereClause($clauseArray)
- * 1522:     function compileFieldCfg($fieldCfg)
+ * 1468:     function compileFieldList($selectFields)
+ * 1510:     function compileFromTables($tablesArray)
+ * 1551:     function compileWhereClause($clauseArray)
+ * 1605:     function compileFieldCfg($fieldCfg)
  *
  *              SECTION: Debugging
- * 1571:     function debug_parseSQLpart($part,$str)
- * 1593:     function debug_parseSQLpartCompare($str,$newStr,$caseInsensitive=FALSE)
- * 1626:     function debug_testSQL($SQLquery)
+ * 1654:     function debug_parseSQLpart($part,$str)
+ * 1679:     function debug_parseSQLpartCompare($str,$newStr,$caseInsensitive=FALSE)
+ * 1712:     function debug_testSQL($SQLquery)
  *
- * TOTAL FUNCTIONS: 34
+ * TOTAL FUNCTIONS: 35
  * (This index is automatically created/updated by the extension "extdeveval")
  *
  */
@@ -125,7 +126,7 @@ class t3lib_sqlparser {
         * @return      array           Result array with all the parts in - or error message string
         * @see compileSQL(), debug_testSQL()
         */
-       function parseSQL($parseString) {
+       public function parseSQL($parseString) {
                        // Prepare variables:
                $parseString = $this->trimSQL($parseString);
                $this->parse_error = '';
@@ -133,7 +134,7 @@ class t3lib_sqlparser {
 
                        // Finding starting keyword of string:
                $_parseString = $parseString;   // Protecting original string...
-               $keyword = $this->nextPart($_parseString, '^(SELECT|UPDATE|INSERT[[:space:]]+INTO|DELETE[[:space:]]+FROM|EXPLAIN|DROP[[:space:]]+TABLE|CREATE[[:space:]]+TABLE|CREATE[[:space:]]+DATABASE|ALTER[[:space:]]+TABLE)[[:space:]]+');
+               $keyword = $this->nextPart($_parseString, '^(SELECT|UPDATE|INSERT[[:space:]]+INTO|DELETE[[:space:]]+FROM|EXPLAIN|DROP[[:space:]]+TABLE|CREATE[[:space:]]+TABLE|CREATE[[:space:]]+DATABASE|ALTER[[:space:]]+TABLE|TRUNCATE[[:space:]]+TABLE)[[:space:]]+');
                $keyword = strtoupper(str_replace(array(' ',"\t","\r","\n"),'',$keyword));
 
                switch($keyword)        {
@@ -173,8 +174,12 @@ class t3lib_sqlparser {
                                        // Parsing CREATE DATABASE query:
                                $result = $this->parseCREATEDATABASE($parseString);
                        break;
+                       case 'TRUNCATETABLE':
+                                       // Parsing TRUNCATE TABLE query:
+                               $result = $this->parseTRUNCATETABLE($parseString);
+                       break;
                        default:
-                               return $this->parseError('"'.$keyword.'" is not a keyword',$parseString);
+                               $result = $this->parseError('"'.$keyword.'" is not a keyword',$parseString);
                        break;
                }
 
@@ -188,11 +193,11 @@ class t3lib_sqlparser {
         * @return      mixed           Returns array with components of SELECT query on success, otherwise an error message string.
         * @see compileSELECT()
         */
-       function parseSELECT($parseString)      {
+       protected function parseSELECT($parseString) {
 
                        // Removing SELECT:
                $parseString = $this->trimSQL($parseString);
-               $parseString = ltrim(substr($parseString,6)); // REMOVE eregi_replace('^SELECT[[:space:]]+','',$parseString);
+               $parseString = ltrim(substr($parseString,6));
 
                        // Init output variable:
                $result = array();
@@ -246,6 +251,9 @@ class t3lib_sqlparser {
                        }
                } 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;
        }
@@ -257,11 +265,11 @@ class t3lib_sqlparser {
         * @return      mixed           Returns array with components of UPDATE query on success, otherwise an error message string.
         * @see compileUPDATE()
         */
-       function parseUPDATE($parseString)      {
+       protected function parseUPDATE($parseString) {
 
                        // Removing UPDATE
                $parseString = $this->trimSQL($parseString);
-               $parseString = ltrim(substr($parseString,6)); // REMOVE eregi_replace('^UPDATE[[:space:]]+','',$parseString);
+               $parseString = ltrim(substr($parseString,6));
 
                        // Init output variable:
                $result = array();
@@ -311,11 +319,11 @@ class t3lib_sqlparser {
         * @return      mixed           Returns array with components of INSERT query on success, otherwise an error message string.
         * @see compileINSERT()
         */
-       function parseINSERT($parseString)      {
+       protected function parseINSERT($parseString) {
 
                        // Removing INSERT
                $parseString = $this->trimSQL($parseString);
-               $parseString = ltrim(substr(ltrim(substr($parseString,6)),4)); // REMOVE eregi_replace('^INSERT[[:space:]]+INTO[[:space:]]+','',$parseString);
+               $parseString = ltrim(substr(ltrim(substr($parseString,6)),4));
 
                        // Init output variable:
                $result = array();
@@ -326,30 +334,54 @@ class t3lib_sqlparser {
 
                if ($result['TABLE'])   {
 
-                       if ($this->nextPart($parseString,'^(VALUES)[[:space:]]+'))      {       // In this case there are no field names mentioned in the SQL!
+                       if ($this->nextPart($parseString,'^(VALUES)([[:space:]]+|\()')) {       // In this case there are no field names mentioned in the SQL!
                                        // Get values/fieldnames (depending...)
                                $result['VALUES_ONLY'] = $this->getValue($parseString,'IN');
-                               if ($this->parse_error) { return $this->parse_error; }
+                               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; }
 
-                               if ($this->nextPart($parseString,'^(VALUES)[[:space:]]+'))      {       // "VALUES" keyword binds the fieldnames to values:
+                               if ($this->nextPart($parseString,'^(VALUES)([[:space:]]+|\()')) {       // "VALUES" keyword binds the fieldnames to values:
+                                       $result['FIELDS'] = array();
+                                       do {
+                                               $values = $this->getValue($parseString, 'IN');  // Using the "getValue" function to get the field list...
+                                               if ($this->parse_error) {
+                                                       return $this->parse_error;
+                                               }
 
-                                       $values = $this->getValue($parseString,'IN');   // Using the "getValue" function to get the field list...
-                                       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, '^(,)') === ',');
 
-                                       foreach($fieldNames as $k => $fN)       {
-                                               if (preg_match('/^[[:alnum:]_]+$/',$fN))        {
-                                                       if (isset($values[$k])) {
-                                                               if (!isset($result['FIELDS'][$fN]))     {
-                                                                       $result['FIELDS'][$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);
+                                       if (count($result['FIELDS']) === 1) {
+                                               $result['FIELDS'] = $result['FIELDS'][0];
+                                       } else {
+                                               $result['EXTENDED'] = '1';
                                        }
                                } else return $this->parseError('VALUES keyword expected',$parseString);
                        }
@@ -371,11 +403,11 @@ class t3lib_sqlparser {
         * @return      mixed           Returns array with components of DELETE query on success, otherwise an error message string.
         * @see compileDELETE()
         */
-       function parseDELETE($parseString)      {
+       protected function parseDELETE($parseString) {
 
                        // Removing DELETE
                $parseString = $this->trimSQL($parseString);
-               $parseString = ltrim(substr(ltrim(substr($parseString,6)),4)); // REMOVE eregi_replace('^DELETE[[:space:]]+FROM[[:space:]]+','',$parseString);
+               $parseString = ltrim(substr(ltrim(substr($parseString,6)),4));
 
                        // Init output variable:
                $result = array();
@@ -409,11 +441,11 @@ class t3lib_sqlparser {
         * @return      mixed           Returns array with components of EXPLAIN query on success, otherwise an error message string.
         * @see parseSELECT()
         */
-       function parseEXPLAIN($parseString)     {
+       protected function parseEXPLAIN($parseString) {
 
                        // Removing EXPLAIN
                $parseString = $this->trimSQL($parseString);
-               $parseString = ltrim(substr($parseString,6)); // REMOVE eregi_replace('^EXPLAIN[[:space:]]+','',$parseString);
+               $parseString = ltrim(substr($parseString,6));
 
                        // Init output variable:
                $result = $this->parseSELECT($parseString);
@@ -431,11 +463,11 @@ class t3lib_sqlparser {
         * @return      mixed           Returns array with components of CREATE TABLE query on success, otherwise an error message string.
         * @see compileCREATETABLE()
         */
-       function parseCREATETABLE($parseString) {
+       protected function parseCREATETABLE($parseString) {
 
                        // Removing CREATE TABLE
                $parseString = $this->trimSQL($parseString);
-               $parseString = ltrim(substr(ltrim(substr($parseString,6)),5)); // REMOVE eregi_replace('^CREATE[[:space:]]+TABLE[[:space:]]+','',$parseString);
+               $parseString = ltrim(substr(ltrim(substr($parseString,6)),5));
 
                        // Init output variable:
                $result = array();
@@ -448,17 +480,24 @@ class t3lib_sqlparser {
 
                                // While the parseString is not yet empty:
                        while(strlen($parseString)>0)   {
-                               if ($key = $this->nextPart($parseString, '^(KEY|PRIMARY KEY)([[:space:]]+|\()'))        {       // Getting key
+                               if ($key = $this->nextPart($parseString, '^(KEY|PRIMARY KEY|UNIQUE KEY|UNIQUE)([[:space:]]+|\()'))      {       // Getting key
                                        $key = strtoupper(str_replace(array(' ',"\t","\r","\n"),'',$key));
 
                                        switch($key)    {
                                                case 'PRIMARYKEY':
-                                                       $result['KEYS'][$key] = $this->getValue($parseString,'_LIST');
+                                                       $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');
+                                                               $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;
@@ -479,7 +518,7 @@ class t3lib_sqlparser {
 
                                // Finding what is after the table definition - table type in MySQL
                        if ($delim==')')        {
-                               if ($this->nextPart($parseString, '^(TYPE[[:space:]]*=)'))      {
+                               if ($this->nextPart($parseString, '^((ENGINE|TYPE)[[:space:]]*=)'))     {
                                        $result['tableType'] = $parseString;
                                        $parseString = '';
                                }
@@ -503,11 +542,11 @@ class t3lib_sqlparser {
         * @return      mixed           Returns array with components of ALTER TABLE query on success, otherwise an error message string.
         * @see compileALTERTABLE()
         */
-       function parseALTERTABLE($parseString)  {
+       protected function parseALTERTABLE($parseString) {
 
                        // Removing ALTER TABLE
                $parseString = $this->trimSQL($parseString);
-               $parseString = ltrim(substr(ltrim(substr($parseString,5)),5)); // REMOVE eregi_replace('^ALTER[[:space:]]+TABLE[[:space:]]+','',$parseString);
+               $parseString = ltrim(substr(ltrim(substr($parseString,5)),5));
 
                        // Init output variable:
                $result = array();
@@ -572,11 +611,11 @@ class t3lib_sqlparser {
         * @param       string          SQL string starting with DROP TABLE
         * @return      mixed           Returns array with components of DROP TABLE query on success, otherwise an error message string.
         */
-       function parseDROPTABLE($parseString)   {
+       protected function parseDROPTABLE($parseString) {
 
                        // Removing DROP TABLE
                $parseString = $this->trimSQL($parseString);
-               $parseString = ltrim(substr(ltrim(substr($parseString,4)),5)); // eregi_replace('^DROP[[:space:]]+TABLE[[:space:]]+','',$parseString);
+               $parseString = ltrim(substr(ltrim(substr($parseString,4)),5));
 
                        // Init output variable:
                $result = array();
@@ -605,11 +644,11 @@ class t3lib_sqlparser {
         * @param       string          SQL string starting with CREATE DATABASE
         * @return      mixed           Returns array with components of CREATE DATABASE query on success, otherwise an error message string.
         */
-       function parseCREATEDATABASE($parseString)      {
+       protected function parseCREATEDATABASE($parseString) {
 
                        // Removing CREATE DATABASE
                $parseString = $this->trimSQL($parseString);
-               $parseString = ltrim(substr(ltrim(substr($parseString,6)),8)); // eregi_replace('^CREATE[[:space:]]+DATABASE[[:space:]]+','',$parseString);
+               $parseString = ltrim(substr(ltrim(substr($parseString,6)),8));
 
                        // Init output variable:
                $result = array();
@@ -629,6 +668,38 @@ class t3lib_sqlparser {
                } else return $this->parseError('No database found!',$parseString);
        }
 
+       /**
+        * Parsing TRUNCATE TABLE query
+        * 
+        * @param       string          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);
+               }
+       }
+
 
 
 
@@ -659,19 +730,24 @@ class t3lib_sqlparser {
         * @return      array           If successful parsing, returns an array, otherwise an error string.
         * @see compileFieldList()
         */
-       function parseFieldList(&$parseString, $stopRegex='')   {
+       public function parseFieldList(&$parseString, $stopRegex = '') {
 
-                       // Prepare variables:
-               $parseString = $this->trimSQL($parseString);
-               $this->lastStopKeyWord = '';
-               $this->parse_error = '';
+               $stack = array();       // Contains the parsed content
 
+               if(strlen($parseString)==0) return $stack;  // FIXME - should never happen, why does it?
 
-               $stack = array();       // Contains the parsed content
                $pnt = 0;                       // Pointer to positions in $stack
                $level = 0;                     // Indicates the parenthesis level we are at.
                $loopExit = 0;          // Recursivity brake.
 
+                       // Prepare variables:
+               $parseString = $this->trimSQL($parseString);
+               $this->lastStopKeyWord = '';
+               $this->parse_error = '';
+
+                       // Parse any SQL hint / comments
+               $stack[$pnt]['comments'] = $this->nextPart($parseString, '^(\/\*.*\*\/)');
+
                        // $parseString is continously shortend by the process and we keep parsing it till it is zero:
                while (strlen($parseString)) {
 
@@ -701,29 +777,41 @@ class t3lib_sqlparser {
                                }
                        } else {        // Outside parenthesis, looking for next field:
 
-                                       // Looking for a known function (only known functions supported)
-                               $func = $this->nextPart($parseString,'^(count|max|min|floor|sum|avg)[[:space:]]*\(');
-                               if ($func)      {
-                                       $parseString = trim(substr($parseString,1));    // Strip of "("
-                                       $stack[$pnt]['type'] = 'function';
-                                       $stack[$pnt]['function'] = $func;
-                                       $level++;       // increse parenthesis level counter.
+                                       // 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 {
-                                               // 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];
+                                               // Looking for a known function (only known functions supported)
+                                       $func = $this->nextPart($parseString,'^(count|max|min|floor|sum|avg)[[:space:]]*\(');
+                                       if ($func)      {
+                                               $parseString = trim(substr($parseString,1));    // Strip of "("
+                                               $stack[$pnt]['type'] = 'function';
+                                               $stack[$pnt]['function'] = $func;
+                                               $level++;       // increse parenthesis level counter.
+                                       } 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 {
-                                                       $stack[$pnt]['table'] = '';
-                                                       $stack[$pnt]['field'] = $tableField[0];
+                                                       return $this->parseError('No field name found as expected in parseFieldList()',$parseString);
                                                }
-                                       } else {
-                                               return $this->parseError('No field name found as expected',$parseString);
                                        }
                                }
                        }
@@ -750,7 +838,7 @@ class t3lib_sqlparser {
 
                                        // Looking for comma (since the stop-keyword did not trigger a return...)
                                if (strlen($parseString) && !$this->nextPart($parseString,'^(,)'))      {
-                                       return $this->parseError('No comma found as expected',$parseString);
+                                       return $this->parseError('No comma found as expected in parseFieldList()',$parseString);
                                }
 
                                        // Increasing pointer:
@@ -760,7 +848,7 @@ class t3lib_sqlparser {
                                // Check recursivity brake:
                        $loopExit++;
                        if ($loopExit>500)      {
-                               return $this->parseError('More than 500 loops, exiting prematurely...',$parseString);
+                               return $this->parseError('More than 500 loops, exiting prematurely in parseFieldList()...',$parseString);
                        }
                }
 
@@ -769,6 +857,41 @@ class t3lib_sqlparser {
        }
 
        /**
+        * Parsing a CASE ... WHEN flow-control construct.
+        * The output from this function can be compiled back with ->compileCaseStatement()
+        *
+        * @param       string          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 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.
         *
@@ -777,7 +900,7 @@ class t3lib_sqlparser {
         * @return      array           If successful parsing, returns an array, otherwise an error string.
         * @see compileFromTables()
         */
-       function parseFromTables(&$parseString, $stopRegex='')  {
+       public function parseFromTables(&$parseString, $stopRegex = '') {
 
                        // Prepare variables:
                $parseString = $this->trimSQL($parseString);
@@ -791,31 +914,68 @@ class t3lib_sqlparser {
                        // $parseString is continously shortend by the process and we keep parsing it till it is zero:
                while (strlen($parseString)) {
                                // Looking for the table:
-                       if ($stack[$pnt]['table'] = $this->nextPart($parseString,'^([[:alnum:]_]+)(,|[[:space:]]+)'))   {
-                           $stack[$pnt]['as'] = $this->nextPart($parseString,'^([[:alnum:]_]+)[[:space:]]*');
-                       } else return $this->parseError('No table name found as expected!',$parseString);
+                       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 = strtoupper(str_replace(array(' ',"\t","\r","\n"), '', $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
-                       if ($join = $this->nextPart($parseString,'^(JOIN|LEFT[[:space:]]+JOIN)[[:space:]]+'))   {
-                               $stack[$pnt]['JOIN']['type'] = $join;
-                               if ($stack[$pnt]['JOIN']['withTable'] = $this->nextPart($parseString,'^([[:alnum:]_]+)[[:space:]]+ON[[:space:]]+',1))   {
-                                       $field1 = $this->nextPart($parseString,'^([[:alnum:]_.]+)[[:space:]]*=[[:space:]]*',1);
+                       $joinCnt = 0;
+                       while ($join = $this->nextPart($parseString,'^(LEFT[[:space:]]+JOIN|LEFT[[:space:]]+OUTER[[:space:]]+JOIN|RIGHT[[:space:]]+JOIN|RIGHT[[:space:]]+OUTER[[:space:]]+JOIN|INNER[[:space:]]+JOIN|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);
+                                       }
+                                       $field1 = $this->nextPart($parseString,'^([[:alnum:]_.]+)[[:space:]]*=[[:space:]]*', 1);
                                        $field2 = $this->nextPart($parseString,'^([[:alnum:]_.]+)[[:space:]]+');
-                                       if ($field1 && $field2) {
-                                               $stack[$pnt]['JOIN']['ON'] = array($field1,$field2);
-                                       } else return $this->parseError('No join fields found!',$parseString);
-                               } else  return $this->parseError('No join table found!',$parseString);
+                                       if ($field1 && $field2) {
+
+                                               // Explode fields into field and table:
+                                               $tableField = explode('.', $field1, 2);
+                                               $field1 = array();
+                                               if (count($tableField) != 2) {
+                                                       $field1['table'] = '';
+                                                       $field1['field'] = $tableField[0];
+                                               } else {
+                                                       $field1['table'] = $tableField[0];
+                                                       $field1['field'] = $tableField[1];
+                                               }
+                                               $tableField = explode('.', $field2, 2);
+                                               $field2 = array();
+                                               if (count($tableField) != 2) {
+                                                       $field2['table'] = '';
+                                                       $field2['field'] = $tableField[0];
+                                               } else {
+                                                       $field2['table'] = $tableField[0];
+                                                       $field2['field'] = $tableField[1];
+                                               }
+                                               $stack[$pnt]['JOIN'][$joinCnt]['ON'] = array($field1, $field2);
+                                               $joinCnt++;
+                                       } else return $this->parseError('No join fields found in parseFromTables()!', $parseString);
+                               } 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 = strtoupper(str_replace(array(' ',"\t","\r","\n"),'',$this->lastStopKeyWord));
+                       if ($stopRegex && $this->lastStopKeyWord = $this->nextPart($parseString, $stopRegex)) {
+                               $this->lastStopKeyWord = strtoupper(str_replace(array(' ',"\t","\r","\n"), '', $this->lastStopKeyWord));
                                return $stack;
                        }
 
                                // Looking for comma:
-                       if (strlen($parseString) && !$this->nextPart($parseString,'^(,)'))      {
-                               return $this->parseError('No comma found as expected',$parseString);
+                       if (strlen($parseString) && !$this->nextPart($parseString, '^(,)')) {
+                               return $this->parseError('No comma found as expected in parseFromTables()', $parseString);
                        }
 
                                // Increasing pointer:
@@ -823,8 +983,8 @@ class t3lib_sqlparser {
 
                                // Check recursivity brake:
                        $loopExit++;
-                       if ($loopExit>500)      {
-                               return $this->parseError('More than 500 loops, exiting prematurely...',$parseString);
+                       if ($loopExit > 500) {
+                               return $this->parseError('More than 500 loops, exiting prematurely in parseFromTables()...', $parseString);
                        }
                }
 
@@ -840,7 +1000,7 @@ class t3lib_sqlparser {
         * @param       string          Regular expressing to STOP parsing, eg. '^(GROUP BY|ORDER BY|LIMIT)([[:space:]]*)'
         * @return      mixed           If successful parsing, returns an array, otherwise an error string.
         */
-       function parseWhereClause(&$parseString, $stopRegex='') {
+       public function parseWhereClause(&$parseString, $stopRegex = '') {
 
                        // Prepare variables:
                $parseString = $this->trimSQL($parseString);
@@ -857,82 +1017,231 @@ class t3lib_sqlparser {
 
                                // Look for next parenthesis level:
                        $newLevel = $this->nextPart($parseString,'^([(])');
-                       if ($newLevel=='(')     {                       // If new level is started, manage stack/pointers:
+                       if ($newLevel == '(') {                 // If new level is started, manage stack/pointers:
                                $level++;                                       // Increase level
                                $pnt[$level] = 0;                       // Reset pointer for this level
                                $stack[$level] = array();       // Reset stack for this level
                        } else {        // If no new level is started, just parse the current level:
 
-                                       // Find "modifyer", eg. "NOT or !"
-                               $stack[$level][$pnt[$level]]['modifier'] = trim($this->nextPart($parseString,'^(!|NOT[[:space:]]+)'));
-
-                                       // Fieldname:
-                               if ($fieldName = $this->nextPart($parseString,'^([[:alnum:]._]+)([[:space:]]+|&|<=|>=|<|>|=|!=|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];
+                                       // 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:]]*');
+                                       $parseString = trim(substr($parseString, 1));   // Strip of "("
+                                       $stack[$level][$pnt[$level]]['func']['subquery'] = $this->parseSELECT($parseString);
+                                               // 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:]]*');
+                                               $parseString = trim(substr($parseString, 1));   // Strip of "("
+                                               $stack[$level][$pnt[$level]]['func']['substr'] = $this->getValue($parseString);
+                                               if (!$this->nextPart($parseString, '^(,)')) {
+                                                       return $this->parseError('No comma found as expected in parseWhereClause()');
+                                               }
+                                               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');
+                                               }
+                                       } elseif (preg_match('/^IFNULL[[:space:]]*[(]/i', $parseString)) {
+                                               $stack[$level][$pnt[$level]]['func']['type'] = $this->nextPart($parseString, '^(IFNULL)[[:space:]]*');
+                                               $parseString = trim(substr($parseString, 1));   // Strip of "("
+                                               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');
+                                               }
+                                       } 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 (strlen($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];
+                                                               }
+                                                       }
+                                               }
+                                       }
+       
+                                               // Find "comparator":
+                                       $stack[$level][$pnt[$level]]['comparator'] = $this->nextPart($parseString, '^(<=|>=|<|>|=|!=|NOT[[:space:]]+IN|IN|NOT[[:space:]]+LIKE|LIKE|IS[[:space:]]+NOT|IS|BETWEEN|NOT[[:space]]+BETWEEN)');
+                                       if (strlen($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 (t3lib_div::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);
+                                                               // Seek to new position in parseString after parsing of the subquery
+                                                       $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 (t3lib_div::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->getValue($parseString, $stack[$level][$pnt[$level]]['comparator']);
+                                                       if ($this->parse_error) {
+                                                               return $this->parse_error;
+                                                       }
+                                               }
                                        }
-                               } else {
-                                       return $this->parseError('No field name found as expected',$parseString);
-                               }
+                               }
 
-                                       // See if the value is calculated. Support only for "&" (boolean AND) at the moment:
-                               $stack[$level][$pnt[$level]]['calc'] = $this->nextPart($parseString,'^(&)');
-                               if (strlen($stack[$level][$pnt[$level]]['calc']))       {
-                                               // Finding value for calculation:
-                                       $stack[$level][$pnt[$level]]['calc_value'] = $this->getValue($parseString);
-                               }
+                                       // Finished, increase pointer:
+                               $pnt[$level]++;
 
-                                       // Find "comparator":
-                               $stack[$level][$pnt[$level]]['comparator'] = $this->nextPart($parseString,'^(<=|>=|<|>|=|!=|NOT[[:space:]]+IN|IN|NOT[[:space:]]+LIKE|LIKE|IS)');
-                               if (strlen($stack[$level][$pnt[$level]]['comparator'])) {
-                                               // Finding value for comparator:
-                                       $stack[$level][$pnt[$level]]['value'] = $this->getValue($parseString,$stack[$level][$pnt[$level]]['comparator']);
-                                       if ($this->parse_error) { return $this->parse_error; }
+                                       // 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];
                                }
 
-                                       // Finished, increase pointer:
-                               $pnt[$level]++;
+                                       // 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,'^([)])'))  {
+                               while ($this->nextPart($parseString,'^([)])')) {
                                        $level--;               // Decrease level:
                                        $stack[$level][$pnt[$level]]['sub'] = $stack[$level+1];         // Copy stack
                                        $pnt[$level]++; // Increase pointer of the new level
 
                                                // Make recursivity check:
                                        $loopExit++;
-                                       if ($loopExit>500)      {
-                                               return $this->parseError('More than 500 loops (in search for exit parenthesis), exiting prematurely...',$parseString);
+                                       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; support for AND, OR and &&):
-                               $op = $this->nextPart($parseString,'^(AND|OR|AND[[:space:]]+NOT)(\(|[[:space:]]+)');
-                               if ($op)        {
+                                       // 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 (strlen($parseString)) {
+                               } elseif (strlen($parseString)) {
 
                                                // Looking for stop-keywords:
-                                       if ($stopRegex && $this->lastStopKeyWord = $this->nextPart($parseString, $stopRegex))   {
-                                               $this->lastStopKeyWord = strtoupper(str_replace(array(' ',"\t","\r","\n"),'',$this->lastStopKeyWord));
+                                       if ($stopRegex && $this->lastStopKeyWord = $this->nextPart($parseString, $stopRegex)) {
+                                               $this->lastStopKeyWord = strtoupper(str_replace(array(' ',"\t","\r","\n"), '', $this->lastStopKeyWord));
                                                return $stack[0];
                                        } else {
-                                               return $this->parseError('No operator, but parsing not finished.',$parseString);
+                                               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...',$parseString);
+                       if ($loopExit > 500) {
+                               return $this->parseError('More than 500 loops, exiting prematurely in parseWhereClause()...', $parseString);
                        }
                }
 
@@ -948,7 +1257,7 @@ class t3lib_sqlparser {
         * @param       string          Regular expressing to STOP parsing, eg. '^(GROUP BY|ORDER BY|LIMIT)([[:space:]]*)'
         * @return      mixed           If successful parsing, returns an array, otherwise an error string.
         */
-       function parseFieldDef(&$parseString, $stopRegex='')    {
+       public function parseFieldDef(&$parseString, $stopRegex = '') {
                        // Prepare variables:
                $parseString = $this->trimSQL($parseString);
                $this->lastStopKeyWord = '';
@@ -957,14 +1266,14 @@ class t3lib_sqlparser {
                $result = array();
 
                        // Field type:
-               if ($result['fieldType'] =  $this->nextPart($parseString,'^(int|smallint|tinyint|mediumint|double|varchar|char|text|tinytext|mediumtext|blob|tinyblob|mediumblob|longblob)([[:space:]]+|\()'))  {
+               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 (substr($parseString,0,1)=='(')      {
                                $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!',$parseString);
+                               } else return $this->parseError('No end-parenthesis for value found in parseFieldDef()!',$parseString);
                        }
 
                                // Looking for keywords
@@ -979,7 +1288,9 @@ class t3lib_sqlparser {
                                        break;
                                }
                        }
-               } else return $this->parseError('Field type unknown!',$parseString);
+               } else {
+                       return $this->parseError('Field type unknown in parseFieldDef()!',$parseString);
+               }
 
                return $result;
        }
@@ -1001,7 +1312,7 @@ class t3lib_sqlparser {
         ************************************/
 
        /**
-        * Strips of a part of the parseString and returns the matching part.
+        * Strips off a part of the parseString and returns the matching part.
         * Helper function for the parsing methods.
         *
         * @param       string          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.
@@ -1009,8 +1320,8 @@ class t3lib_sqlparser {
         * @param       boolean         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.
         */
-       function nextPart(&$parseString,$regex,$trimAll=FALSE)  {
-               //if (eregi($regex,$parseString.' ', $reg))     {       // Adding space char because [[:space:]]+ is often a requirement in regex's
+       protected function nextPart(&$parseString, $regex, $trimAll = FALSE) {
+               $reg = array();
                if (preg_match('/'.$regex.'/i',$parseString.' ', $reg)) {       // Adding space char because [[:space:]]+ is often a requirement in regex's
                        $parseString = ltrim(substr($parseString,strlen($reg[$trimAll?0:1])));
                        return $reg[1];
@@ -1022,10 +1333,12 @@ class t3lib_sqlparser {
         *
         * @param       string          The parseString, eg. "(0,1,2,3) ..." or "('asdf','qwer') ..." or "1234 ..." or "'My string value here' ..."
         * @param       string          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)
-        * @return      string          The value (string/integer). Otherwise an array with error message in first key (0)
+        * @param       string          The mode, eg. "INDEX"
+        * @return      mixed           The value (string/integer). Otherwise an array with error message in first key (0)
         */
-       function getValue(&$parseString,$comparator='') {
-               //if (t3lib_div::inList('NOTIN,IN,_LIST',strtoupper(ereg_replace('[[:space:]]','',$comparator))))       {       // List of values:
+       protected function getValue(&$parseString, $comparator = '', $mode = '') {
+               $value = '';
+
                if (t3lib_div::inList('NOTIN,IN,_LIST',strtoupper(str_replace(array(' ',"\n","\r","\t"),'',$comparator))))      {       // List of values:
                        if ($this->nextPart($parseString,'^([(])'))     {
                                $listValues = array();
@@ -1033,6 +1346,10 @@ class t3lib_sqlparser {
 
                                while($comma==',')      {
                                        $listValues[] = $this->getValue($parseString);
+                                       if ($mode === 'INDEX') {
+                                                       // Remove any length restriction on INDEX definition
+                                               $this->nextPart($parseString, '^([(]\d+[)])');
+                                       }
                                        $comma = $this->nextPart($parseString,'^([,])');
                                }
 
@@ -1054,22 +1371,23 @@ class t3lib_sqlparser {
 
                                // Quote?
                        $firstChar = substr($parseString,0,1);
-
                        switch($firstChar)      {
                                case '"':
-                                       return array($this->getValueInQuotes($parseString,'"'),'"');
+                                       $value = array($this->getValueInQuotes($parseString,'"'),'"');
                                break;
                                case "'":
-                                       return array($this->getValueInQuotes($parseString,"'"),"'");
+                                       $value = array($this->getValueInQuotes($parseString,"'"),"'");
                                break;
                                default:
+                                       $reg = array();
                                        if (preg_match('/^([[:alnum:]._-]+)/i',$parseString, $reg))     {
                                                $parseString = ltrim(substr($parseString,strlen($reg[0])));
-                                               return array($reg[1]);
+                                               $value = array($reg[1]);
                                        }
                                break;
                        }
                }
+               return $value;
        }
 
        /**
@@ -1080,17 +1398,16 @@ class t3lib_sqlparser {
         * @param       string          The quote used; input either " or '
         * @return      string          The value, passed through stripslashes() !
         */
-       function getValueInQuotes(&$parseString,$quote) {
+       protected function getValueInQuotes(&$parseString, $quote) {
 
                $parts = explode($quote,substr($parseString,1));
                $buffer = '';
                foreach($parts as $k => $v)     {
                        $buffer.=$v;
 
-                       unset($reg);
-                       //preg_match('/[\]*$/',$v,$reg); // does not work. what is the *exact* meaning of the next line?
-                       ereg('[\]*$',$v,$reg);
-                       if (strlen($reg[0])%2)  {
+                       $reg = array();
+                       preg_match('/\\\\$/', $v, $reg);
+                       if ($reg AND strlen($reg[0])%2) {
                                $buffer.=$quote;
                        } else {
                                $parseString = ltrim(substr($parseString,strlen($buffer)+2));
@@ -1106,7 +1423,7 @@ class t3lib_sqlparser {
         * @param       string          Input string
         * @return      string          Output string
         */
-       function parseStripslashes($str)        {
+       protected function parseStripslashes($str) {
                $search = array('\\\\', '\\\'', '\\"', '\0', '\n', '\r', '\Z');
                $replace = array('\\', '\'', '"', "\x00", "\x0a", "\x0d", "\x1a");
 
@@ -1120,8 +1437,7 @@ class t3lib_sqlparser {
         * @param       string          Input string
         * @return      string          Output string
         */
-       function compileAddslashes($str)        {
-return $str;
+       protected function compileAddslashes($str) {
                $search = array('\\', '\'', '"', "\x00", "\x0a", "\x0d", "\x1a");
                $replace = array('\\\\', '\\\'', '\\"', '\0', '\n', '\r', '\Z');
 
@@ -1135,7 +1451,7 @@ return $str;
         * @param       string          Remaining query to parse.
         * @return      string          Error message.
         */
-       function parseError($msg,$restQuery)    {
+       protected function parseError($msg, $restQuery) {
                $this->parse_error = 'SQL engine parse ERROR: '.$msg.': near "'.substr($restQuery,0,50).'"';
                return $this->parse_error;
        }
@@ -1149,9 +1465,8 @@ return $str;
         * @param       string          Input string
         * @return      string          Output string
         */
-       function trimSQL($str)  {
+       protected function trimSQL($str) {
                return trim(rtrim($str, "; \r\n\t")).' ';
-               //return trim(ereg_replace('[[:space:];]*$','',$str)).' ';
        }
 
 
@@ -1178,7 +1493,7 @@ return $str;
         * @return      string          SQL query
         * @see parseSQL()
         */
-       function compileSQL($components)        {
+       public function compileSQL($components) {
                switch($components['type'])     {
                        case 'SELECT':
                                $query = $this->compileSELECT($components);
@@ -1204,6 +1519,9 @@ return $str;
                        case 'ALTERTABLE':
                                $query = $this->compileALTERTABLE($components);
                        break;
+                       case 'TRUNCATETABLE':
+                               $query = $this->compileTRUNCATETABLE($components);
+                       break;
                }
 
                return $query;
@@ -1216,7 +1534,7 @@ return $str;
         * @return      string          SQL SELECT query
         * @see parseSELECT()
         */
-       function compileSELECT($components)     {
+       protected function compileSELECT($components) {
 
                        // Initialize:
                $where = $this->compileWhereClause($components['WHERE']);
@@ -1247,7 +1565,7 @@ return $str;
         * @return      string          SQL UPDATE query
         * @see parseUPDATE()
         */
-       function compileUPDATE($components)     {
+       protected function compileUPDATE($components) {
 
                        // Where clause:
                $where = $this->compileWhereClause($components['WHERE']);
@@ -1275,35 +1593,37 @@ return $str;
         * @return      string          SQL INSERT query
         * @see parseINSERT()
         */
-       function compileINSERT($components)     {
+       protected function compileINSERT($components) {
+               $values = array();
 
-               if ($components['VALUES_ONLY']) {
-                               // Initialize:
-                       $fields = array();
-                       foreach($components['VALUES_ONLY'] as $fV)      {
-                               $fields[]=$fV[1].$this->compileAddslashes($fV[0]).$fV[1];
-                       }
-
-                               // Make query:
-                       $query = 'INSERT INTO '.$components['TABLE'].'
-                                       VALUES
-                                       ('.implode(',
-                                       ',$fields).')';
+               if (isset($components['VALUES_ONLY']) && is_array($components['VALUES_ONLY'])) {
+                       $valuesComponents = $components['EXTENDED'] === '1' ? $components['VALUES_ONLY'] : array($components['VALUES_ONLY']);
+                       $tableFields = array();
                } else {
-                               // Initialize:
+                       $valuesComponents = $components['EXTENDED'] === '1' ? $components['FIELDS'] : array($components['FIELDS']);
+                       $tableFields = array_keys($valuesComponents[0]);
+               }
+
+               foreach ($valuesComponents as $valuesComponent) {
                        $fields = array();
-                       foreach($components['FIELDS'] as $fN => $fV)    {
-                               $fields[$fN]=$fV[1].$this->compileAddslashes($fV[0]).$fV[1];
+                       foreach ($valuesComponent as $fV) {
+                               $fields[] = $fV[1] . $this->compileAddslashes($fV[0]) . $fV[1];
                        }
+                       $values[] = '(' . implode(',
+                               ', $fields) . ')';
+               }
 
-                               // Make query:
-                       $query = 'INSERT INTO '.$components['TABLE'].'
-                                       ('.implode(',
-                                       ',array_keys($fields)).')
-                                       VALUES
-                                       ('.implode(',
-                                       ',$fields).')';
+                       // Make query:
+               $query = 'INSERT INTO ' . $components['TABLE'];
+               if (count($tableFields)) {
+                       $query .= '
+                               (' . implode(',
+                               ', $tableFields) . ')';
                }
+               $query .= '
+                       VALUES
+                       ' . implode(',
+                       ', $values);
 
                return $query;
        }
@@ -1315,7 +1635,7 @@ return $str;
         * @return      string          SQL DELETE query
         * @see parseDELETE()
         */
-       function compileDELETE($components)     {
+       protected function compileDELETE($components) {
 
                        // Where clause:
                $where = $this->compileWhereClause($components['WHERE']);
@@ -1335,7 +1655,7 @@ return $str;
         * @return      string          SQL CREATE TABLE query
         * @see parseCREATETABLE()
         */
-       function compileCREATETABLE($components)        {
+       protected function compileCREATETABLE($components) {
 
                        // Create fields and keys:
                $fieldsKeys = array();
@@ -1345,6 +1665,8 @@ return $str;
                foreach($components['KEYS'] as $kN => $kCfg)    {
                        if ($kN == 'PRIMARYKEY')        {
                                $fieldsKeys[]='PRIMARY KEY ('.implode(',', $kCfg).')';
+                       } elseif ($kN == 'UNIQUE')      {
+                               $fieldsKeys[]='UNIQUE '.$kN.' ('.implode(',', $kCfg).')';
                        } else {
                                $fieldsKeys[]='KEY '.$kN.' ('.implode(',', $kCfg).')';
                        }
@@ -1366,7 +1688,7 @@ return $str;
         * @return      string          SQL ALTER TABLE query
         * @see parseALTERTABLE()
         */
-       function compileALTERTABLE($components) {
+       protected function compileALTERTABLE($components) {
 
                        // Make query:
                $query = 'ALTER TABLE '.$components['TABLE'].' '.$components['action'].' '.($components['FIELD']?$components['FIELD']:$components['KEY']);
@@ -1392,6 +1714,22 @@ return $str;
                return $query;
        }
 
+       /**
+        * Compiles a TRUNCATE TABLE statement from components array
+        * 
+        * @param       array           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;
+       }
+
 
 
 
@@ -1416,16 +1754,18 @@ return $str;
         * Can also compile field lists for ORDER BY and GROUP BY.
         *
         * @param       array           Array of select fields, (made with ->parseFieldList())
+        * @param       boolean         Whether comments should be compiled
         * @return      string          Select field string
         * @see parseFieldList()
         */
-       function compileFieldList($selectFields)        {
+       public function compileFieldList($selectFields, $compileComments = TRUE) {
 
                        // Prepare buffer variable:
-               $outputParts = array();
+               $fields = '';
 
                        // Traverse the selectFields if any:
                if (is_array($selectFields))    {
+                       $outputParts = array();
                        foreach($selectFields as $k => $v)      {
 
                                        // Detecting type:
@@ -1433,8 +1773,13 @@ return $str;
                                        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['table']?$v['table'].'.':'').$v['field'];
+                                               $outputParts[$k] = ($v['distinct']?$v['distinct']:'').($v['table']?$v['table'].'.':'').$v['field'];
                                        break;
                                }
 
@@ -1448,10 +1793,41 @@ return $str;
                                        $outputParts[$k].= ' '.$v['sortDir'];
                                }
                        }
+                       if ($compileComments && $selectFields[0]['comments']) {
+                               $fields = $selectFields[0]['comments'] . ' ';
+                       }
+                       $fields .= implode(', ', $outputParts);
                }
 
-                       // Return imploded buffer:
-               return implode(', ',$outputParts);
+               return $fields;
+       }
+
+       /**
+        * Compiles a CASE ... WHEN flow-control construct based on input array (made with ->parseCaseStatement())
+        *
+        * @param       array           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;
        }
 
        /**
@@ -1461,81 +1837,122 @@ return $str;
         * @return      string          Table name string
         * @see parseFromTables()
         */
-       function compileFromTables($tablesArray)        {
+       public function compileFromTables($tablesArray) {
 
                        // Prepare buffer variable:
                $outputParts = array();
 
                        // Traverse the table names:
-               if (is_array($tablesArray))     {
-                       foreach($tablesArray as $k => $v)       {
+               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 ($v['as']) {
+                                       $outputParts[$k] .= ' ' . $v['as_keyword'] . ' ' . $v['as'];
                                }
 
-                               if (is_array($v['JOIN']))       {
-                                       $outputParts[$k].= ' '.$v['JOIN']['type'].' '.$v['JOIN']['withTable'].' ON '.implode('=',$v['JOIN']['ON']);
+                               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 ';
+                                               $outputParts[$k] .= ($join['ON'][0]['table']) ? $join['ON'][0]['table'] . '.' : '';
+                                               $outputParts[$k] .= $join['ON'][0]['field'];
+                                               $outputParts[$k] .= '=';
+                                               $outputParts[$k] .= ($join['ON'][1]['table']) ? $join['ON'][1]['table'] . '.' : '';
+                                               $outputParts[$k] .= $join['ON'][1]['field'];
+                                       }
                                }
-
                        }
                }
 
                        // Return imploded buffer:
-               return implode(', ',$outputParts);
+               return implode(', ', $outputParts);
        }
 
        /**
         * Implodes an array of WHERE clause configuration into a WHERE clause.
-        * NOTICE: MIGHT BY A TEMPORARY FUNCTION. Use for debugging only!
-        * BUT IT IS NEEDED FOR DBAL - MAKE IT PERMANENT?!?!
         *
         * @param       array           WHERE clause configuration
         * @return      string          WHERE clause as string.
         * @see explodeWhereClause()
         */
-       function compileWhereClause($clauseArray)       {
+       public function compileWhereClause($clauseArray) {
 
                        // Prepare buffer variable:
-               $output='';
+               $output = '';
 
                        // Traverse clause array:
-               if (is_array($clauseArray))     {
-                       foreach($clauseArray as $k => $v)       {
+               if (is_array($clauseArray)) {
+                       foreach ($clauseArray as $k => $v) {
 
                                        // Set operator:
-                               $output.=$v['operator'] ? ' '.$v['operator'] : '';
+                               $output .= $v['operator'] ? ' ' . $v['operator'] : '';
 
                                        // Look for sublevel:
-                               if (is_array($v['sub']))        {
-                                       $output.=' ('.trim($this->compileWhereClause($v['sub'])).')';
+                               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 {
 
-                                               // 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];
+                                       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 .= ')';
+                                       } 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'];
+                                       if ($v['comparator']) {
+                                               $output .= ' ' . $v['comparator'];
 
                                                        // Detecting value type; list or plain:
-                                               if (t3lib_div::inList('NOTIN,IN',strtoupper(str_replace(array(' ',"\t","\r","\n"),'',$v['comparator']))))       {
-                                                       $valueBuffer = array();
-                                                       foreach($v['value'] as $realValue)      {
-                                                               $valueBuffer[]=$realValue[1].$this->compileAddslashes($realValue[0]).$realValue[1];
+                                               if (t3lib_div::inList('NOTIN,IN', strtoupper(str_replace(array(' ', "\t", "\r", "\n"), '', $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 (t3lib_div::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.=' ('.trim(implode(',',$valueBuffer)).')';
+                                                       $output .= ' ' . $v['value']['operator'] . '(' . implode(',', $values) . ')';
                                                } else {
-                                                       $output.=' '.$v['value'][1].$this->compileAddslashes($v['value'][0]).$v['value'][1];
+                                                       $output .= ' ' . $v['value'][1] . $this->compileAddslashes($v['value'][0]) . $v['value'][1];
                                                }
                                        }
                                }
@@ -1552,7 +1969,7 @@ return $str;
         * @param       array           Field definition parts
         * @return      string          Field definition string
         */
-       function compileFieldCfg($fieldCfg)     {
+       public function compileFieldCfg($fieldCfg) {
 
                        // Set type:
                $cfg = $fieldCfg['fieldType'];
@@ -1601,18 +2018,21 @@ return $str;
         * @param       string          SQL string to verify parsability of
         * @return      mixed           Returns array with string 1 and 2 if error, otherwise false
         */
-       function debug_parseSQLpart($part,$str) {
+       public function debug_parseSQLpart($part, $str) {
+               $retVal = false;
+
                switch($part)   {
                        case 'SELECT':
-                               return $this->debug_parseSQLpartCompare($str,$this->compileFieldList($this->parseFieldList($str)));
+                               $retVal = $this->debug_parseSQLpartCompare($str,$this->compileFieldList($this->parseFieldList($str)));
                        break;
                        case 'FROM':
-                               return $this->debug_parseSQLpartCompare($str,$this->compileFromTables($this->parseFromTables($str)));
+                               $retVal = $this->debug_parseSQLpartCompare($str,$this->compileFromTables($this->parseFromTables($str)));
                        break;
                        case 'WHERE':
-                               return $this->debug_parseSQLpartCompare($str,$this->compileWhereClause($this->parseWhereClause($str)));
+                               $retVal = $this->debug_parseSQLpartCompare($str,$this->compileWhereClause($this->parseWhereClause($str)));
                        break;
                }
+               return $retVal;
        }
 
        /**
@@ -1623,7 +2043,7 @@ return $str;
         * @param       boolean         If true, the strings are compared insensitive to case
         * @return      mixed           Returns array with string 1 and 2 if error, otherwise false
         */
-       function debug_parseSQLpartCompare($str,$newStr,$caseInsensitive=FALSE) {
+       public function debug_parseSQLpartCompare($str, $newStr, $caseInsensitive = FALSE) {
                if ($caseInsensitive)   {
                        $str1 = strtoupper($str);
                        $str2 = strtoupper($newStr);
@@ -1656,9 +2076,7 @@ return $str;
         * @param       string          SQL query
         * @return      string          Query if all is well, otherwise exit.
         */
-       function debug_testSQL($SQLquery)       {
-#              return $SQLquery;
-#debug(array($SQLquery));
+       public function debug_testSQL($SQLquery) {
 
                        // Getting result array:
                $parseResult = $this->parseSQL($SQLquery);
@@ -1690,4 +2108,5 @@ return $str;
 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_sqlparser.php'])        {
        include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_sqlparser.php']);
 }
-?>
+
+?>
\ No newline at end of file