function smf_db_error($db_string, $connection = null) { global $txt, $context, $sourcedir, $webmaster_email, $modSettings; global $forum_version, $db_connection, $db_last_error, $db_persist; global $db_server, $db_user, $db_passwd, $db_name, $db_show_debug, $ssi_db_user, $ssi_db_passwd; global $smcFunc; // Get the file and line numbers. list($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__); // Decide which connection to use $connection = $connection == null ? $db_connection : $connection; // This is the error message... $query_error = mysql_error($connection); $query_errno = mysql_errno($connection); // Error numbers: // 1016: Can't open file '....MYI' // 1030: Got error ??? from table handler. // 1034: Incorrect key file for table. // 1035: Old key file for table. // 1205: Lock wait timeout exceeded. // 1213: Deadlock found. // 2006: Server has gone away. // 2013: Lost connection to server during query. // Log the error. if ($query_errno != 1213 && $query_errno != 1205 && function_exists('log_error')) { log_error($txt['database_error'] . ': ' . $query_error . (!empty($modSettings['enableErrorQueryLogging']) ? "\n\n{$db_string}" : ''), 'database', $file, $line); } // Database error auto fixing ;). if (function_exists('cache_get_data') && (!isset($modSettings['autoFixDatabase']) || $modSettings['autoFixDatabase'] == '1')) { // Force caching on, just for the error checking. $old_cache = @$modSettings['cache_enable']; $modSettings['cache_enable'] = '1'; if (($temp = cache_get_data('db_last_error', 600)) !== null) { $db_last_error = max(@$db_last_error, $temp); } if (@$db_last_error < time() - 3600 * 24 * 3) { // We know there's a problem... but what? Try to auto detect. if ($query_errno == 1030 && strpos($query_error, ' 127 ') !== false) { preg_match_all('~(?:[\\n\\r]|^)[^\']+?(?:FROM|JOIN|UPDATE|TABLE) ((?:[^\\n\\r(]+?(?:, )?)*)~s', $db_string, $matches); $fix_tables = array(); foreach ($matches[1] as $tables) { $tables = array_unique(explode(',', $tables)); foreach ($tables as $table) { // Now, it's still theoretically possible this could be an injection. So backtick it! if (trim($table) != '') { $fix_tables[] = '`' . strtr(trim($table), array('`' => '')) . '`'; } } } $fix_tables = array_unique($fix_tables); } elseif ($query_errno == 1016) { if (preg_match('~\'([^\\.\']+)~', $query_error, $match) != 0) { $fix_tables = array('`' . $match[1] . '`'); } } elseif ($query_errno == 1034 || $query_errno == 1035) { preg_match('~\'([^\']+?)\'~', $query_error, $match); $fix_tables = array('`' . $match[1] . '`'); } } // Check for errors like 145... only fix it once every three days, and send an email. (can't use empty because it might not be set yet...) if (!empty($fix_tables)) { // Subs-Admin.php for updateSettingsFile(), Subs-Post.php for sendmail(). require_once $sourcedir . '/Subs-Admin.php'; require_once $sourcedir . '/Subs-Post.php'; // Make a note of the REPAIR... cache_put_data('db_last_error', time(), 600); if (($temp = cache_get_data('db_last_error', 600)) === null) { updateSettingsFile(array('db_last_error' => time())); } // Attempt to find and repair the broken table. foreach ($fix_tables as $table) { $smcFunc['db_query']('', "\r\n\t\t\t\t\tREPAIR TABLE {$table}", false, false); } // And send off an email! sendmail($webmaster_email, $txt['database_error'], $txt['tried_to_repair']); $modSettings['cache_enable'] = $old_cache; // Try the query again...? $ret = $smcFunc['db_query']('', $db_string, false, false); if ($ret !== false) { return $ret; } } else { $modSettings['cache_enable'] = $old_cache; } // Check for the "lost connection" or "deadlock found" errors - and try it just one more time. if (in_array($query_errno, array(1205, 1213, 2006, 2013))) { if (in_array($query_errno, array(2006, 2013)) && $db_connection == $connection) { // Are we in SSI mode? If so try that username and password first if (SMF == 'SSI' && !empty($ssi_db_user) && !empty($ssi_db_passwd)) { if (empty($db_persist)) { $db_connection = @mysql_connect($db_server, $ssi_db_user, $ssi_db_passwd); } else { $db_connection = @mysql_pconnect($db_server, $ssi_db_user, $ssi_db_passwd); } } // Fall back to the regular username and password if need be if (!$db_connection) { if (empty($db_persist)) { $db_connection = @mysql_connect($db_server, $db_user, $db_passwd); } else { $db_connection = @mysql_pconnect($db_server, $db_user, $db_passwd); } } if (!$db_connection || !@mysql_select_db($db_name, $db_connection)) { $db_connection = false; } } if ($db_connection) { // Try a deadlock more than once more. for ($n = 0; $n < 4; $n++) { $ret = $smcFunc['db_query']('', $db_string, false, false); $new_errno = mysql_errno($db_connection); if ($ret !== false || in_array($new_errno, array(1205, 1213))) { break; } } // If it failed again, shucks to be you... we're not trying it over and over. if ($ret !== false) { return $ret; } } } elseif ($query_errno == 1030 && (strpos($query_error, ' -1 ') !== false || strpos($query_error, ' 28 ') !== false || strpos($query_error, ' 12 ') !== false)) { if (!isset($txt)) { $query_error .= ' - check database storage space.'; } else { if (!isset($txt['mysql_error_space'])) { loadLanguage('Errors'); } $query_error .= !isset($txt['mysql_error_space']) ? ' - check database storage space.' : $txt['mysql_error_space']; } } } // Nothing's defined yet... just die with it. if (empty($context) || empty($txt)) { die($query_error); } // Show an error message, if possible. $context['error_title'] = $txt['database_error']; if (allowedTo('admin_forum')) { $context['error_message'] = nl2br($query_error) . '<br />' . $txt['file'] . ': ' . $file . '<br />' . $txt['line'] . ': ' . $line; } else { $context['error_message'] = $txt['try_again']; } // A database error is often the sign of a database in need of upgrade. Check forum versions, and if not identical suggest an upgrade... (not for Demo/CVS versions!) if (allowedTo('admin_forum') && !empty($forum_version) && $forum_version != 'SMF ' . @$modSettings['smfVersion'] && strpos($forum_version, 'Demo') === false && strpos($forum_version, 'CVS') === false) { $context['error_message'] .= '<br /><br />' . sprintf($txt['database_error_versions'], $forum_version, $modSettings['smfVersion']); } if (allowedTo('admin_forum') && isset($db_show_debug) && $db_show_debug === true) { $context['error_message'] .= '<br /><br />' . nl2br($db_string); } // It's already been logged... don't log it again. fatal_error($context['error_message'], false); }
/** * Database error! * Backtrace, log, try to fix. * * @param string $db_string * @param resource $connection = null */ function smf_db_error($db_string, $connection = null) { global $txt, $context, $sourcedir, $webmaster_email, $modSettings; global $forum_version, $db_connection, $db_last_error, $db_persist; global $db_server, $db_user, $db_passwd, $db_name, $db_show_debug, $ssi_db_user, $ssi_db_passwd; global $smcFunc; // We'll try recovering the file and line number the original db query was called from. list($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__); // Decide which connection to use $connection = $connection === null ? $db_connection : $connection; // This is the error message... $query_error = @pg_last_error($connection); // Log the error. if (function_exists('log_error')) { log_error($txt['database_error'] . ': ' . $query_error . (!empty($modSettings['enableErrorQueryLogging']) ? "\n\n" . $db_string : ''), 'database', $file, $line); } // Nothing's defined yet... just die with it. if (empty($context) || empty($txt)) { die($query_error); } // Show an error message, if possible. $context['error_title'] = $txt['database_error']; if (allowedTo('admin_forum')) { $context['error_message'] = nl2br($query_error) . '<br />' . $txt['file'] . ': ' . $file . '<br />' . $txt['line'] . ': ' . $line; } else { $context['error_message'] = $txt['try_again']; } // A database error is often the sign of a database in need of updgrade. Check forum versions, and if not identical suggest an upgrade... (not for Demo/CVS versions!) if (allowedTo('admin_forum') && !empty($forum_version) && $forum_version != 'SMF ' . @$modSettings['smfVersion'] && strpos($forum_version, 'Demo') === false && strpos($forum_version, 'CVS') === false) { $context['error_message'] .= '<br /><br />' . sprintf($txt['database_error_versions'], $forum_version, $modSettings['smfVersion']); } if (allowedTo('admin_forum') && isset($db_show_debug) && $db_show_debug === true) { $context['error_message'] .= '<br /><br />' . nl2br($db_string); } // It's already been logged... don't log it again. fatal_error($context['error_message'], false); }
function smf_db_error($db_string, $connection = null) { global $txt, $context, $sourcedir, $webmaster_email, $modSettings; global $forum_version, $db_connection, $db_last_error, $db_persist; global $db_server, $db_user, $db_passwd, $db_name, $db_show_debug, $ssi_db_user, $ssi_db_passwd; global $smcFunc; // We'll try recovering the file and line number the original db query was called from. list($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__); // Decide which connection to use $connection = $connection == null ? $db_connection : $connection; // This is the error message... $query_errno = sqlite_last_error($connection); $query_error = sqlite_error_string($query_errno); // Get the extra error message. $errStart = strrpos($db_string, '#!#'); $query_error .= '<br />' . substr($db_string, $errStart + 3); $db_string = substr($db_string, 0, $errStart); // Log the error. if (function_exists('log_error')) { log_error($txt['database_error'] . ': ' . $query_error . (!empty($modSettings['enableErrorQueryLogging']) ? "\n\n" . $db_string : ''), 'database', $file, $line); } // Sqlite optimizing - the actual error message isn't helpful or user friendly. if (strpos($query_error, 'no_access') !== false || strpos($query_error, 'database schema has changed') !== false) { if (!empty($context) && !empty($txt) && !empty($txt['error_sqlite_optimizing'])) { fatal_error($txt['error_sqlite_optimizing'], false); } else { // Don't cache this page! header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-cache'); // Send the right error codes. header('HTTP/1.1 503 Service Temporarily Unavailable'); header('Status: 503 Service Temporarily Unavailable'); header('Retry-After: 3600'); die('Sqlite is optimizing the database, the forum can not be accessed until it has finished. Please try refreshing this page momentarily.'); } } // Nothing's defined yet... just die with it. if (empty($context) || empty($txt)) { die($query_error); } // Show an error message, if possible. $context['error_title'] = $txt['database_error']; if (allowedTo('admin_forum')) { $context['error_message'] = nl2br($query_error) . '<br />' . $txt['file'] . ': ' . $file . '<br />' . $txt['line'] . ': ' . $line; } else { $context['error_message'] = $txt['try_again']; } // A database error is often the sign of a database in need of updgrade. Check forum versions, and if not identical suggest an upgrade... (not for Demo/CVS versions!) if (allowedTo('admin_forum') && !empty($forum_version) && $forum_version != 'SMF ' . @$modSettings['smfVersion'] && strpos($forum_version, 'Demo') === false && strpos($forum_version, 'CVS') === false) { $context['error_message'] .= '<br /><br />' . sprintf($txt['database_error_versions'], $forum_version, $modSettings['smfVersion']); } if (allowedTo('admin_forum') && isset($db_show_debug) && $db_show_debug === true) { $context['error_message'] .= '<br /><br />' . nl2br($db_string); } // It's already been logged... don't log it again. fatal_error($context['error_message'], false); }
function smf_db_replacement__callback($matches) { global $db_callback, $user_info, $db_prefix; list($values, $connection) = $db_callback; if ($matches[1] === 'db_prefix') { return $db_prefix; } if ($matches[1] === 'query_see_board') { return $user_info['query_see_board']; } if ($matches[1] === 'query_wanna_see_board') { return $user_info['query_wanna_see_board']; } if (!isset($matches[2])) { smf_db_error_backtrace('Invalid value inserted or no type specified.', '', E_USER_ERROR, __FILE__, __LINE__); } if (!isset($values[$matches[2]])) { smf_db_error_backtrace('The database value you\'re trying to insert does not exist: ' . htmlspecialchars($matches[2]), '', E_USER_ERROR, __FILE__, __LINE__); } $replacement = $values[$matches[2]]; switch ($matches[1]) { case 'int': if (!is_numeric($replacement) || (string) $replacement !== (string) (int) $replacement) { smf_db_error_backtrace('Wrong value type sent to the database. Integer expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); } return (string) (int) $replacement; break; case 'string': case 'text': return sprintf('\'%1$s\'', mysql_real_escape_string($replacement, $connection)); break; case 'array_int': if (is_array($replacement)) { if (empty($replacement)) { smf_db_error_backtrace('Database error, given array of integer values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); } foreach ($replacement as $key => $value) { if (!is_numeric($value) || (string) $value !== (string) (int) $value) { smf_db_error_backtrace('Wrong value type sent to the database. Array of integers expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); } $replacement[$key] = (string) (int) $value; } return implode(', ', $replacement); } else { smf_db_error_backtrace('Wrong value type sent to the database. Array of integers expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); } break; case 'array_string': if (is_array($replacement)) { if (empty($replacement)) { smf_db_error_backtrace('Database error, given array of string values is empty. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); } foreach ($replacement as $key => $value) { $replacement[$key] = sprintf('\'%1$s\'', mysql_real_escape_string($value, $connection)); } return implode(', ', $replacement); } else { smf_db_error_backtrace('Wrong value type sent to the database. Array of strings expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); } break; case 'date': if (preg_match('~^(\\d{4})-([0-1]?\\d)-([0-3]?\\d)$~', $replacement, $date_matches) === 1) { return sprintf('\'%04d-%02d-%02d\'', $date_matches[1], $date_matches[2], $date_matches[3]); } else { smf_db_error_backtrace('Wrong value type sent to the database. Date expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); } break; case 'float': if (!is_numeric($replacement)) { smf_db_error_backtrace('Wrong value type sent to the database. Floating point number expected. (' . $matches[2] . ')', '', E_USER_ERROR, __FILE__, __LINE__); } return (string) (double) $replacement; break; case 'identifier': // Backticks inside identifiers are supported as of MySQL 4.1. We don't need them for SMF. return '`' . strtr($replacement, array('`' => '', '.' => '')) . '`'; break; case 'raw': return $replacement; break; default: smf_db_error_backtrace('Undefined type used in the database query. (' . $matches[1] . ':' . $matches[2] . ')', '', false, __FILE__, __LINE__); break; } }