Fixed bug #11599: TYPO3 dies without an error message when the mysql-module for php...
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_db.php
old mode 100755 (executable)
new mode 100644 (file)
index 65dcc55..e64e38d
@@ -2,7 +2,7 @@
 /***************************************************************
 *  Copyright notice
 *
-*  (c) 2004-2007 Kasper Skaarhoj (kasperYYYY@typo3.com)
+*  (c) 2004-2009 Kasper Skaarhoj (kasperYYYY@typo3.com)
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
@@ -25,7 +25,9 @@
 *  This copyright notice MUST APPEAR in all copies of the script!
 ***************************************************************/
 /**
- * Contains the class "t3lib_db" containing functions for building SQL queries and mysql wrappers, thus providing a foundational API to all database interaction.
+ * Contains the class "t3lib_db" containing functions for building SQL queries
+ * and mysql wrappers, thus providing a foundational API to all database
+ * interaction.
  * This class is instantiated globally as $TYPO3_DB in TYPO3 scripts.
  *
  * $Id$
@@ -147,6 +149,9 @@ class t3lib_DB {
                // Default link identifier:
        var $link = FALSE;
 
+               // Default character set, applies unless character set or collation are explicitely set
+       var $default_charset = 'utf8';
+
 
 
 
@@ -231,7 +236,7 @@ class t3lib_DB {
                        $this->debug('exec_SELECTquery');
                }
                if ($this->explainOutput) {
-                       $this->explain($query, $this->sql_num_rows($res));
+                       $this->explain($query, $from_table, $this->sql_num_rows($res));
                }
 
                return $res;
@@ -265,7 +270,7 @@ class t3lib_DB {
                $mmWhere.= ($local_table AND $foreign_table) ? ' AND ' : '';
                $mmWhere.= $foreign_table ? ($foreign_table_as ? $foreign_table_as : $foreign_table).'.uid='.$mm_table.'.uid_foreign' : '';
 
-               return $GLOBALS['TYPO3_DB']->exec_SELECTquery(
+               return $this->exec_SELECTquery(
                                        $select,
                                        ($local_table ? $local_table.',' : '').$mm_table.($foreign_table ? ','. $foreign_table.($foreign_table_as ? ' AS '.$foreign_table_as : '') : ''),
                                        $mmWhere.' '.$whereClause,              // whereClauseMightContainGroupOrderBy
@@ -327,6 +332,24 @@ class t3lib_DB {
                return $output;
        }
 
+       /**
+        * Counts the number of rows in a table.
+        *
+        * @param       string          $field: Name of the field to use in the COUNT() expression (e.g. '*')
+        * @param       string          $table: Name of the table to count rows for
+        * @param       string          $where: (optional) WHERE statement of the query
+        * @return      mixed           Number of rows counter (integer) or false if something went wrong (boolean)
+        */
+       public function exec_SELECTcountRows($field, $table, $where = '') {
+               $count = false;
+               $resultSet = $this->exec_SELECTquery('COUNT(' . $field . ')', $table, $where);
+               if ($resultSet !== false) {
+                       list($count) = $this->sql_fetch_row($resultSet);
+                       $this->sql_free_result($resultSet);
+               }
+               return $count;
+       }
+
 
 
 
@@ -351,7 +374,6 @@ class t3lib_DB {
         * @param       array           See exec_INSERTquery()
         * @param       string/array            See fullQuoteArray()
         * @return      string          Full SQL query for INSERT (unless $fields_values does not contain any elements in which case it will be false)
-        * @deprecated                  use exec_INSERTquery() instead if possible!
         */
        function INSERTquery($table,$fields_values,$no_quote_fields=FALSE)      {
 
@@ -386,7 +408,6 @@ class t3lib_DB {
         * @param       array           See exec_UPDATEquery()
         * @param       array           See fullQuoteArray()
         * @return      string          Full SQL query for UPDATE (unless $fields_values does not contain any elements in which case it will be false)
-        * @deprecated                  use exec_UPDATEquery() instead if possible!
         */
        function UPDATEquery($table,$where,$fields_values,$no_quote_fields=FALSE)       {
 
@@ -427,7 +448,6 @@ class t3lib_DB {
         * @param       string          See exec_DELETEquery()
         * @param       string          See exec_DELETEquery()
         * @return      string          Full SQL query for DELETE
-        * @deprecated                  use exec_DELETEquery() instead if possible!
         */
        function DELETEquery($table,$where)     {
                if (is_string($where))  {
@@ -456,7 +476,6 @@ class t3lib_DB {
         * @param       string          See exec_SELECTquery()
         * @param       string          See exec_SELECTquery()
         * @return      string          Full SQL query for SELECT
-        * @deprecated                  use exec_SELECTquery() instead if possible!
         */
        function SELECTquery($select_fields,$from_table,$where_clause,$groupBy='',$orderBy='',$limit='')        {
 
@@ -500,8 +519,9 @@ class t3lib_DB {
         * @return      string          WHERE clause for a query
         */
        function listQuery($field, $value, $table)      {
-               $command = $this->quoteStr($value, $table);
-               $where = '('.$field.' LIKE \'%,'.$command.',%\' OR '.$field.' LIKE \''.$command.',%\' OR '.$field.' LIKE \'%,'.$command.'\' OR '.$field.'=\''.$command.'\')';
+               $pattern = $this->quoteStr($value, $table);
+               $patternForLike = $this->escapeStrForLike($pattern, $table);
+               $where = '('.$field.' LIKE \'%,'.$patternForLike.',%\' OR '.$field.' LIKE \''.$patternForLike.',%\' OR '.$field.' LIKE \'%,'.$patternForLike.'\' OR '.$field.'=\''.$pattern.'\')';
                return $where;
        }
 
@@ -741,12 +761,13 @@ class t3lib_DB {
        /**
         * Executes query
         * mysql() wrapper function
-        * DEPRECATED - use exec_* functions from this class instead!
-        * Usage count/core: 9
+        * Usage count/core: 0
         *
         * @param       string          Database name
         * @param       string          Query to execute
         * @return      pointer         Result pointer / DBAL object
+        * @deprecated since TYPO3 3.6
+        * @see sql_query()
         */
        function sql($db,$query)        {
                $res = mysql_query($query, $this->link);
@@ -780,12 +801,22 @@ 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);
@@ -895,16 +926,37 @@ class t3lib_DB {
        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);
+               @ini_set('track_errors', 1);
+               @ini_set('html_errors', 0);
+
+                       // check if MySQL extension is loaded
+               if (!extension_loaded('mysql')) {
+                       t3lib_BEfunc::typo3PrintError('Database Error', 'You don\'t seem to have MySQL-support for PHP installed!');
+                       exit;
+               }
+
+                       // Check for client compression
+               $isLocalhost = ($TYPO3_db_host == 'localhost' || $TYPO3_db_host == '127.0.0.1');
                if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['no_pconnect'])  {
-                       $this->link = @mysql_connect($TYPO3_db_host, $TYPO3_db_username, $TYPO3_db_password);
+                       if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['dbClientCompress'] && !$isLocalhost) {
+                                       // We use PHP's default value for 4th parameter (new_link), which is false.
+                                       // See PHP sources, for example: file php-5.2.5/ext/mysql/php_mysql.c, function php_mysql_do_connect(), near line 525
+                               $this->link = @mysql_connect($TYPO3_db_host, $TYPO3_db_username, $TYPO3_db_password, false, MYSQL_CLIENT_COMPRESS);
+                       } else {
+                               $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);
+                       if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['dbClientCompress'] && !$isLocalhost) {
+                                       // See comment about 4th parameter in block above
+                               $this->link = @mysql_pconnect($TYPO3_db_host, $TYPO3_db_username, $TYPO3_db_password, false, MYSQL_CLIENT_COMPRESS);
+                       } 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');
+               @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.': '.$error_msg,'Core',4);
@@ -976,16 +1028,20 @@ 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;
                        }
+
+                       $this->sql_free_result($tables_result);
                }
+
                return $whichTables;
        }
 
@@ -1000,11 +1056,13 @@ 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;
                }
 
+               $this->sql_free_result($columns_res);
+
                return $output;
        }
 
@@ -1018,11 +1076,37 @@ 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;
                }
 
+               $this->sql_free_result($keyRes);
+
+               return $output;
+       }
+
+       /**
+        * 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;
+                       }
+
+                       $this->sql_free_result($columns_res);
+               }
+
                return $output;
        }
 
@@ -1104,12 +1188,12 @@ class t3lib_DB {
 
                $error = $this->sql_error();
                if ($error)     {
-                       echo t3lib_div::view_array(array(
+                       debug(array(
                                'caller' => 't3lib_DB::'.$func,
                                'ERROR' => $error,
                                'lastBuiltQuery' => ($query ? $query : $this->debug_lastBuiltQuery),
                                'debug_backtrace' => t3lib_div::debug_trail()
-                       ));
+                       ), 'SQL debug');
                }
        }
 
@@ -1123,7 +1207,7 @@ class t3lib_DB {
                if (!$res) {
                        $trace = FALSE;
                        $msg = 'Invalid database result resource detected';
-                       $trace = debug_backtrace();
+                       $trace = debug_backtrace();
                        array_shift($trace);
                        $cnt = count($trace);
                        for ($i=0; $i<$cnt; $i++)       {
@@ -1132,7 +1216,17 @@ class t3lib_DB {
                        }
                        $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);
+                       // Send to devLog if enabled
+                       if (TYPO3_DLOG) {
+                               $debugLogData = array(
+                                       'SQL Error' => $this->sql_error(),
+                                       'Backtrace' => $trace,
+                               );
+                               if ($this->debug_lastBuiltQuery) {
+                                       $debugLogData = array('SQL Query' => $this->debug_lastBuiltQuery) + $debugLogData;
+                               }
+                               t3lib_div::devLog($msg . '.', 'Core/t3lib_db', 3, $debugLogData);
+                       }
 
                        return FALSE;
                }
@@ -1147,10 +1241,11 @@ class t3lib_DB {
         * 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,$row_count)   {
+       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
@@ -1160,28 +1255,32 @@ class t3lib_DB {
                        return false;
                }
 
-               $error = $GLOBALS['TYPO3_DB']->sql_error();
+               $error = $this->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'])) {
+               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
 
-                       $res = $this->sql_query('SHOW INDEX FROM '.$from_table, $this->link);
-                       if (is_resource($res)) {
-                               while ($tempRow = $this->sql_fetch_assoc($res)) {
-                                       $indices_output[] = $tempRow;
+                       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);
                                }
-                               $this->sql_free_result($res);
                        }
                } else {
                        $debug = false;
@@ -1215,7 +1314,7 @@ class t3lib_DB {
                                        $data['explain'] = $explain_output;
                                }
                                if (count($indices_output)) {
-                                       $data['indices'] = $indices;
+                                       $data['indices'] = $indices_output;
                                }
                                $GLOBALS['TT']->setTSselectQuery($data);
                        }
@@ -1231,4 +1330,5 @@ class t3lib_DB {
 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_db.php'])       {
        include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_db.php']);
 }
-?>
+
+?>
\ No newline at end of file