Fixed bug #8518: Wrong JavaScript inclusion in t3lib_TCEforms
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_db.php
index 8bd17ef..4305609 100755 (executable)
@@ -2,7 +2,7 @@
 /***************************************************************
 *  Copyright notice
 *
-*  (c) 2004-2006 Kasper Skaarhoj (kasperYYYY@typo3.com)
+*  (c) 2004-2008 Kasper Skaarhoj (kasperYYYY@typo3.com)
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
@@ -142,10 +142,14 @@ class t3lib_DB {
        var $debugOutput = FALSE;               // Set "TRUE" if you want database errors outputted.
        var $debug_lastBuiltQuery = '';         // Internally: Set to last built query (not necessarily executed...)
        var $store_lastBuiltQuery = FALSE;      // Set "TRUE" if you want the last built query to be stored in $debug_lastBuiltQuery independent of $this->debugOutput
+       var $explainOutput = 0;                 // Set this to 1 to get queries explained (devIPmask must match). Set the value to 2 to the same but disregarding the devIPmask. There is an alternative option to enable explain output in the admin panel under "TypoScript", which will produce much nicer output, but only works in FE.
 
                // Default link identifier:
        var $link = FALSE;
 
+               // Default character set, applies unless character set or collation are explicitely set
+       var $default_charset = 'utf8';
+
 
 
 
@@ -223,8 +227,16 @@ class t3lib_DB {
         * @return      pointer         MySQL result pointer / DBAL object
         */
        function exec_SELECTquery($select_fields,$from_table,$where_clause,$groupBy='',$orderBy='',$limit='')   {
-               $res = mysql_query($this->SELECTquery($select_fields,$from_table,$where_clause,$groupBy,$orderBy,$limit), $this->link);
-               if ($this->debugOutput) $this->debug('exec_SELECTquery');
+               $query = $this->SELECTquery($select_fields,$from_table,$where_clause,$groupBy,$orderBy,$limit);
+               $res = mysql_query($query, $this->link);
+
+               if ($this->debugOutput) {
+                       $this->debug('exec_SELECTquery');
+               }
+               if ($this->explainOutput) {
+                       $this->explain($query, $from_table, $this->sql_num_rows($res));
+               }
+
                return $res;
        }
 
@@ -551,11 +563,7 @@ class t3lib_DB {
         * @see quoteStr()
         */
        function fullQuoteStr($str, $table)     {
-               if (function_exists('mysql_real_escape_string'))        {
-                       return '\''.mysql_real_escape_string($str, $this->link).'\'';
-               } else {
-                       return '\''.mysql_escape_string($str).'\'';
-               }
+               return '\''.mysql_real_escape_string($str, $this->link).'\'';
        }
 
        /**
@@ -595,11 +603,7 @@ class t3lib_DB {
         * @see quoteStr()
         */
        function quoteStr($str, $table) {
-               if (function_exists('mysql_real_escape_string'))        {
-                       return mysql_real_escape_string($str, $this->link);
-               } else {
-                       return mysql_escape_string($str);
-               }
+               return mysql_real_escape_string($str, $this->link);
        }
 
        /**
@@ -749,7 +753,7 @@ class t3lib_DB {
         */
        function sql($db,$query)        {
                $res = mysql_query($query, $this->link);
-               if ($this->debugOutput) $this->debug('sql');
+               if ($this->debugOutput) $this->debug('sql',$query);
                return $res;
        }
 
@@ -763,7 +767,7 @@ class t3lib_DB {
         */
        function sql_query($query)      {
                $res = mysql_query($query, $this->link);
-               if ($this->debugOutput) $this->debug('sql_query');
+               if ($this->debugOutput) $this->debug('sql_query',$query);
                return $res;
        }
 
@@ -779,14 +783,25 @@ class t3lib_DB {
        }
 
        /**
+        * Returns the error number on the last sql() execution
+        * mysql_errno() wrapper function
+        *
+        * @return      int             MySQL error number.
+        */
+       function sql_errno() {
+               return mysql_errno($this->link);
+       }
+
+       /**
         * Returns the number of selected rows.
         * mysql_num_rows() wrapper function
         * Usage count/core: 85
         *
         * @param       pointer         MySQL result pointer (of SELECT query) / DBAL object
-        * @return      integer         Number of resulting rows.
+        * @return      integer         Number of resulting rows
         */
        function sql_num_rows($res)     {
+               $this->debug_check_recordset($res);
                return mysql_num_rows($res);
        }
 
@@ -799,6 +814,7 @@ class t3lib_DB {
         * @return      array           Associative array of result row.
         */
        function sql_fetch_assoc($res)  {
+               $this->debug_check_recordset($res);
                return mysql_fetch_assoc($res);
        }
 
@@ -812,6 +828,7 @@ class t3lib_DB {
         * @return      array           Array with result rows.
         */
        function sql_fetch_row($res)    {
+               $this->debug_check_recordset($res);
                return mysql_fetch_row($res);
        }
 
@@ -824,6 +841,7 @@ class t3lib_DB {
         * @return      boolean         Returns TRUE on success or FALSE on failure.
         */
        function sql_free_result($res)  {
+               $this->debug_check_recordset($res);
                return mysql_free_result($res);
        }
 
@@ -859,6 +877,7 @@ class t3lib_DB {
         * @return      boolean         Returns TRUE on success or FALSE on failure.
         */
        function sql_data_seek($res,$seek)      {
+               $this->debug_check_recordset($res);
                return mysql_data_seek($res,$seek);
        }
 
@@ -872,6 +891,7 @@ class t3lib_DB {
         * @return      string          Returns the name of the specified field index
         */
        function sql_field_type($res,$pointer)  {
+               $this->debug_check_recordset($res);
                return mysql_field_type($res,$pointer);
        }
 
@@ -886,22 +906,30 @@ class t3lib_DB {
         * @return      pointer         Returns a positive MySQL persistent link identifier on success, or FALSE on error.
         */
        function sql_pconnect($TYPO3_db_host, $TYPO3_db_username, $TYPO3_db_password)   {
+                       // mysql_error() is tied to an established connection
+                       // if the connection fails we need a different method to get the error message
+               @ini_set('track_errors', 1);
+               @ini_set('html_errors', 0);
                if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['no_pconnect'])  {
                        $this->link = @mysql_connect($TYPO3_db_host, $TYPO3_db_username, $TYPO3_db_password);
                } else {
                        $this->link = @mysql_pconnect($TYPO3_db_host, $TYPO3_db_username, $TYPO3_db_password);
                }
+               $error_msg = $php_errormsg;
+               @ini_restore('track_errors');
+               @ini_restore('html_errors');
 
                if (!$this->link) {
-                       t3lib_div::sysLog('Could not connect to Mysql server '.$TYPO3_db_host.' with user '.$TYPO3_db_username.'.','Core',4);
+                       t3lib_div::sysLog('Could not connect to MySQL server '.$TYPO3_db_host.' with user '.$TYPO3_db_username.': '.$error_msg,'Core',4);
                } else {
-                       $setDBinit = t3lib_div::trimExplode(chr(10), $GLOBALS['TYPO3_CONF_VARS']['SYS']['setDBinit'],true);
+                       $setDBinit = t3lib_div::trimExplode(chr(10), $GLOBALS['TYPO3_CONF_VARS']['SYS']['setDBinit'],TRUE);
                        foreach ($setDBinit as $v)      {
                                if (mysql_query($v, $this->link) === FALSE)     {
                                        t3lib_div::sysLog('Could not initialize DB connection with query "'.$v.'": '.mysql_error($this->link),'Core',3);
                                }
                        }
                }
+
                return $this->link;
        }
 
@@ -916,7 +944,7 @@ class t3lib_DB {
        function sql_select_db($TYPO3_db)       {
                $ret = @mysql_select_db($TYPO3_db, $this->link);
                if (!$ret) {
-                       t3lib_div::sysLog('Could not select Mysql database '.$TYPO3_db.': '.mysql_error(),'Core',4);
+                       t3lib_div::sysLog('Could not select MySQL database '.$TYPO3_db.': '.mysql_error(),'Core',4);
        }
                return $ret;
        }
@@ -961,16 +989,18 @@ class t3lib_DB {
         * In a DBAL this method should 1) look up all tables from the DBMS  of the _DEFAULT handler and then 2) add all tables *configured* to be managed by other handlers
         * Usage count/core: 2
         *
-        * @return      array           Tables in an array (tablename is in both key and value)
+        * @return      array           Array with tablenames as key and arrays with status information as value
         */
        function admin_get_tables()     {
                $whichTables = array();
-               $tables_result = mysql_list_tables(TYPO3_db, $this->link);
+
+               $tables_result = mysql_query('SHOW TABLE STATUS FROM `'.TYPO3_db.'`', $this->link);
                if (!mysql_error())     {
                        while ($theTable = mysql_fetch_assoc($tables_result)) {
-                               $whichTables[current($theTable)] = current($theTable);
+                               $whichTables[$theTable['Name']] = $theTable;
                        }
                }
+
                return $whichTables;
        }
 
@@ -985,7 +1015,7 @@ class t3lib_DB {
        function admin_get_fields($tableName)   {
                $output = array();
 
-               $columns_res = mysql_query('SHOW columns FROM '.$tableName, $this->link);
+               $columns_res = mysql_query('SHOW COLUMNS FROM `'.$tableName.'`', $this->link);
                while($fieldRow = mysql_fetch_assoc($columns_res))      {
                        $output[$fieldRow['Field']] = $fieldRow;
                }
@@ -1003,7 +1033,7 @@ class t3lib_DB {
        function admin_get_keys($tableName)     {
                $output = array();
 
-               $keyRes = mysql_query('SHOW keys FROM '.$tableName, $this->link);
+               $keyRes = mysql_query('SHOW KEYS FROM `'.$tableName.'`', $this->link);
                while($keyRow = mysql_fetch_assoc($keyRes))     {
                        $output[] = $keyRow;
                }
@@ -1012,6 +1042,28 @@ class t3lib_DB {
        }
 
        /**
+        * Returns information about the character sets supported by the current DBM
+        * This function is important not only for the Install Tool but probably for DBALs as well since they might need to look up table specific information in order to construct correct queries. In such cases this information should probably be cached for quick delivery.
+        *
+        * This is used by the Install Tool to convert tables tables with non-UTF8 charsets
+        * Use in Install Tool only!
+        *
+        * @return      array           Array with Charset as key and an array of "Charset", "Description", "Default collation", "Maxlen" as values
+        */
+       function admin_get_charsets()   {
+               $output = array();
+
+               $columns_res = mysql_query('SHOW CHARACTER SET', $this->link);
+               if ($columns_res) {
+                       while (($row = mysql_fetch_assoc($columns_res))) {
+                               $output[$row['Charset']] = $row;
+                       }
+               }
+
+               return $output;
+       }
+
+       /**
         * mysql() wrapper function, used by the Install Tool and EM for all queries regarding management of the database!
         * Usage count/core: 10
         *
@@ -1020,7 +1072,7 @@ class t3lib_DB {
         */
        function admin_query($query)    {
                $res = mysql_query($query, $this->link);
-               if ($this->debugOutput) $this->debug('admin_query');
+               if ($this->debugOutput) $this->debug('admin_query',$query);
                return $res;
        }
 
@@ -1082,20 +1134,139 @@ class t3lib_DB {
         * Debug function: Outputs error if any
         *
         * @param       string          Function calling debug()
+        * @param       string          Last query if not last built query
         * @return      void
         */
-       function debug($func)   {
+       function debug($func, $query='')        {
 
                $error = $this->sql_error();
-               if ($error)             {
-                       echo t3lib_div::view_array(array(
+               if ($error)     {
+                       debug(array(
                                'caller' => 't3lib_DB::'.$func,
                                'ERROR' => $error,
-                               'lastBuiltQuery' => $this->debug_lastBuiltQuery,
+                               'lastBuiltQuery' => ($query ? $query : $this->debug_lastBuiltQuery),
                                'debug_backtrace' => t3lib_div::debug_trail()
-                       ));
+                       ), 'SQL debug');
                }
        }
+
+       /**
+        * Checks if recordset is valid and writes debugging inormation into devLog if not.
+        *
+        * @param       resource        $res    Recordset
+        * @return      boolean <code>false</code> if recordset is not valid
+        */
+       function debug_check_recordset($res) {
+               if (!$res) {
+                       $trace = FALSE;
+                       $msg = 'Invalid database result resource detected';
+                       $trace = debug_backtrace();
+                       array_shift($trace);
+                       $cnt = count($trace);
+                       for ($i=0; $i<$cnt; $i++)       {
+                                       // complete objects are too large for the log
+                               if (isset($trace['object']))    unset($trace['object']);
+                       }
+                       $msg .= ': function t3lib_DB->' . $trace[0]['function'] . ' called from file ' . substr($trace[0]['file'],strlen(PATH_site)+2) . ' in line ' . $trace[0]['line'];
+                       t3lib_div::sysLog($msg.'. Use a devLog extension to get more details.', 'Core/t3lib_db', 3);
+                       t3lib_div::devLog($msg.'.', 'Core/t3lib_db', 3, $trace);
+
+                       return FALSE;
+               }
+               return TRUE;
+       }
+
+       /**
+        * Explain select queries
+        * If $this->explainOutput is set, SELECT queries will be explained here. Only queries with more than one possible result row will be displayed.
+        * The output is either printed as raw HTML output or embedded into the TS admin panel (checkbox must be enabled!)
+        *
+        * TODO: Feature is not DBAL-compliant
+        *
+        * @param       string          SQL query
+        * @param       string          Table(s) from which to select. This is what comes right after "FROM ...". Required value.
+        * @param       integer         Number of resulting rows
+        * @return      boolean         True if explain was run, false otherwise
+        */
+       protected function explain($query,$from_table,$row_count)       {
+
+               if ((int)$this->explainOutput==1 || ((int)$this->explainOutput==2 && t3lib_div::cmpIP(t3lib_div::getIndpEnv('REMOTE_ADDR'), $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask']))) {
+                       $explainMode = 1;       // raw HTML output
+               } elseif ((int)$this->explainOutput==3 && is_object($GLOBALS['TT'])) {
+                       $explainMode = 2;       // embed the output into the TS admin panel
+               } else {
+                       return false;
+               }
+
+               $error = $GLOBALS['TYPO3_DB']->sql_error();
+               $trail = t3lib_div::debug_trail();
+
+               $explain_tables = array();
+               $explain_output = array();
+               $res = $this->sql_query('EXPLAIN '.$query, $this->link);
+               if (is_resource($res)) {
+                       while ($tempRow = $this->sql_fetch_assoc($res)) {
+                               $explain_output[] = $tempRow;
+                               $explain_tables[] = $tempRow['table'];
+                       }
+                       $this->sql_free_result($res);
+               }
+
+               $indices_output = array();
+               if ($explain_output[0]['rows']>1 || t3lib_div::inList('ALL',$explain_output[0]['type'])) {      // Notice: Rows are skipped if there is only one result, or if no conditions are set
+                       $debug = true;  // only enable output if it's really useful
+
+                       foreach ($explain_tables as $table) {
+                               $res = $this->sql_query('SHOW INDEX FROM '.$table, $this->link);
+                               if (is_resource($res)) {
+                                       while ($tempRow = $this->sql_fetch_assoc($res)) {
+                                               $indices_output[] = $tempRow;
+                                       }
+                                       $this->sql_free_result($res);
+                               }
+                       }
+               } else {
+                       $debug = false;
+               }
+
+               if ($debug) {
+                       if ($explainMode==1) {
+                               t3lib_div::debug('QUERY: '.$query);
+                               t3lib_div::debug(array('Debug trail:'=>$trail), 'Row count: '.$row_count);
+
+                               if ($error) {
+                                       t3lib_div::debug($error);
+                               }
+                               if (count($explain_output)) {
+                                       t3lib_div::debug($explain_output);
+                               }
+                               if (count($indices_output)) {
+                                       t3lib_div::debugRows($indices_output);
+                               }
+
+                       } elseif ($explainMode==2) {
+                               $data = array();
+                               $data['query'] = $query;
+                               $data['trail'] = $trail;
+                               $data['row_count'] = $row_count;
+
+                               if ($error) {
+                                       $data['error'] = $error;
+                               }
+                               if (count($explain_output)) {
+                                       $data['explain'] = $explain_output;
+                               }
+                               if (count($indices_output)) {
+                                       $data['indices'] = $indices_output;
+                               }
+                               $GLOBALS['TT']->setTSselectQuery($data);
+                       }
+                       return true;
+               }
+
+               return false;
+       }
+
 }