| Line # | Frequency | Source Line |
| 1 | | <?php |
| 2 | | // $Id: database.mysql.inc,v 1.90 2008/02/17 19:39:11 dries Exp $ |
| 3 | | |
| 4 | | /** |
| 5 | | * @file |
| 6 | | * Database interface code for MySQL database servers. |
| 7 | | */ |
| 8 | | |
| 9 | | /** |
| 10 | | * @ingroup database |
| 11 | | * @{ |
| 12 | | */ |
| 13 | | |
| 14 | | // Include functions shared between mysql and mysqli. |
| 15 | | require_once './includes/database.mysql-common.inc'; |
| 16 | | |
| 17 | | /** |
| 18 | | * Report database status. |
| 19 | | */ |
| 20 | | function db_status_report($phase) { |
| 21 | | $t = get_t(); |
| 22 | | |
| 23 | | $version = db_version(); |
| 24 | | |
| 25 | | $form['mysql'] = array( |
| 26 | | 'title' => $t('MySQL database'), |
| 27 | | 'value' => ($phase == 'runtime') ? l($version, 'admin/reports/status/sql') : $version, |
| 28 | | ); |
| 29 | | |
| 30 | | if (version_compare($version, DRUPAL_MINIMUM_MYSQL) < 0) { |
| 31 | | $form['mysql']['severity'] = REQUIREMENT_ERROR; |
| 32 | | $form['mysql']['description'] = $t('Your MySQL Server is too old. Drupal requires at least MySQL %version.', array('%version' => DRUPAL_MINIMUM_MYSQL)); |
| 33 | | } |
| 34 | | |
| 35 | | return $form; |
| 36 | | } |
| 37 | | |
| 38 | | /** |
| 39 | | * Returns the version of the database server currently in use. |
| 40 | | * |
| 41 | | * @return Database server version |
| 42 | | */ |
| 43 | | function db_version() { |
| 44 | | list($version) = explode('-', mysql_get_server_info()); |
| 45 | | return $version; |
| 46 | | } |
| 47 | | |
| 48 | | /** |
| 49 | | * Initialize a database connection. |
| 50 | | */ |
| 51 | | function db_connect($url) { |
| 52 | | $url = parse_url($url); |
| 53 | | |
| 54 | | // Check if MySQL support is present in PHP |
| 55 | | if (!function_exists('mysql_connect')) { |
| 56 | | _db_error_page('Unable to use the MySQL database because the MySQL extension for PHP is not installed. Check your <code>php.ini</code> to see how you can enable it.'); |
| 57 | | } |
| 58 | | |
| 59 | | // Decode url-encoded information in the db connection string |
| 60 | | $url['user'] = urldecode($url['user']); |
| 61 | | // Test if database url has a password. |
| 62 | | $url['pass'] = isset($url['pass']) ? urldecode($url['pass']) : ''; |
| 63 | | $url['host'] = urldecode($url['host']); |
| 64 | | $url['path'] = urldecode($url['path']); |
| 65 | | |
| 66 | | // Allow for non-standard MySQL port. |
| 67 | | if (isset($url['port'])) { |
| 68 | | $url['host'] = $url['host'] .':'. $url['port']; |
| 69 | | } |
| 70 | | |
| 71 | | // - TRUE makes mysql_connect() always open a new link, even if |
| 72 | | // mysql_connect() was called before with the same parameters. |
| 73 | | // This is important if you are using two databases on the same |
| 74 | | // server. |
| 75 | | // - 2 means CLIENT_FOUND_ROWS: return the number of found |
| 76 | | // (matched) rows, not the number of affected rows. |
| 77 | | $connection = @mysql_connect($url['host'], $url['user'], $url['pass'], TRUE, 2); |
| 78 | | if (!$connection || !mysql_select_db(substr($url['path'], 1))) { |
| 79 | | // Show error screen otherwise |
| 80 | | _db_error_page(mysql_error()); |
| 81 | | } |
| 82 | | // Require ANSI mode to improve SQL portability. |
| 83 | | mysql_query("SET SESSION sql_mode='ANSI'", $connection); |
| 84 | | // Force UTF-8. |
| 85 | | mysql_query('SET NAMES "utf8"', $connection); |
| 86 | | return $connection; |
| 87 | | } |
| 88 | | |
| 89 | | /** |
| 90 | | * Helper function for db_query(). |
| 91 | | */ |
| 92 | | function _db_query($query, $debug = 0) { |
| 93 | 1 | global $active_db, $queries, $user; |
| 94 | | |
| 95 | 1 | if (variable_get('dev_query', 0)) { |
| 96 | | list($usec, $sec) = explode(' ', microtime()); |
| 97 | | $timer = (float)$usec + (float)$sec; |
| 98 | | // If devel.module query logging is enabled, prepend a comment with the username and calling function |
| 99 | | // to the SQL string. This is useful when running mysql's SHOW PROCESSLIST to learn what exact |
| 100 | | // code is issueing the slow query. |
| 101 | | $bt = debug_backtrace(); |
| 102 | | // t() may not be available yet so we don't wrap 'Anonymous'. |
| 103 | | $name = $user->uid ? $user->name : variable_get('anonymous', 'Anonymous'); |
| 104 | | // str_replace() to prevent SQL injection via username or anonymous name. |
| 105 | | $name = str_replace(array('*', '/'), '', $name); |
| 106 | | $query = '/* '. $name .' : '. $bt[2]['function'] .' */ '. $query; |
| 107 | | } |
| 108 | | |
| 109 | 1 | $result = mysql_query($query, $active_db); |
| 110 | | |
| 111 | 1 | if (variable_get('dev_query', 0)) { |
| 112 | | $query = $bt[2]['function'] ."\n". $query; |
| 113 | | list($usec, $sec) = explode(' ', microtime()); |
| 114 | | $stop = (float)$usec + (float)$sec; |
| 115 | | $diff = $stop - $timer; |
| 116 | | $queries[] = array($query, $diff); |
| 117 | | } |
| 118 | | |
| 119 | 1 | if ($debug) { |
| 120 | | print '<p>query: '. $query .'<br />error:'. mysql_error($active_db) .'</p>'; |
| 121 | | } |
| 122 | | |
| 123 | 1 | if (!mysql_errno($active_db)) { |
| 124 | 1 | return $result; |
| 125 | | } |
| 126 | | else { |
| 127 | | // Indicate to drupal_error_handler that this is a database error. |
| 128 | | ${DB_ERROR} = TRUE; |
| 129 | | trigger_error(check_plain(mysql_error($active_db) ."\nquery: ". $query), E_USER_WARNING); |
| 130 | | return FALSE; |
| 131 | | } |
| 132 | | } |
| 133 | | |
| 134 | | /** |
| 135 | | * Fetch one result row from the previous query as an object. |
| 136 | | * |
| 137 | | * @param $result |
| 138 | | * A database query result resource, as returned from db_query(). |
| 139 | | * @return |
| 140 | | * An object representing the next row of the result, or FALSE. The attributes |
| 141 | | * of this object are the table fields selected by the query. |
| 142 | | */ |
| 143 | | function db_fetch_object($result) { |
| 144 | 1 | if ($result) { |
| 145 | 1 | return mysql_fetch_object($result); |
| 146 | | } |
| 147 | | } |
| 148 | | |
| 149 | | /** |
| 150 | | * Fetch one result row from the previous query as an array. |
| 151 | | * |
| 152 | | * @param $result |
| 153 | | * A database query result resource, as returned from db_query(). |
| 154 | | * @return |
| 155 | | * An associative array representing the next row of the result, or FALSE. |
| 156 | | * The keys of this object are the names of the table fields selected by the |
| 157 | | * query, and the values are the field values for this result row. |
| 158 | | */ |
| 159 | | function db_fetch_array($result) { |
| 160 | 1 | if ($result) { |
| 161 | 1 | return mysql_fetch_array($result, MYSQL_ASSOC); |
| 162 | | } |
| 163 | | } |
| 164 | | |
| 165 | | /** |
| 166 | | * Return an individual result field from the previous query. |
| 167 | | * |
| 168 | | * Only use this function if exactly one field is being selected; otherwise, |
| 169 | | * use db_fetch_object() or db_fetch_array(). |
| 170 | | * |
| 171 | | * @param $result |
| 172 | | * A database query result resource, as returned from db_query(). |
| 173 | | * @return |
| 174 | | * The resulting field or FALSE. |
| 175 | | */ |
| 176 | | function db_result($result) { |
| 177 | 1 | if ($result && mysql_num_rows($result) > 0) { |
| 178 | | // The mysql_fetch_row function has an optional second parameter $row |
| 179 | | // but that can't be used for compatibility with Oracle, DB2, etc. |
| 180 | 1 | $array = mysql_fetch_row($result); |
| 181 | 1 | return $array[0]; |
| 182 | | } |
| 183 | 1 | return FALSE; |
| 184 | | } |
| 185 | | |
| 186 | | /** |
| 187 | | * Determine whether the previous query caused an error. |
| 188 | | */ |
| 189 | | function db_error() { |
| 190 | | global $active_db; |
| 191 | | return mysql_errno($active_db); |
| 192 | | } |
| 193 | | |
| 194 | | /** |
| 195 | | * Determine the number of rows changed by the preceding query. |
| 196 | | */ |
| 197 | | function db_affected_rows() { |
| 198 | 1 | global $active_db; |
| 199 | 1 | return mysql_affected_rows($active_db); |
| 200 | | } |
| 201 | | |
| 202 | | /** |
| 203 | | * Runs a limited-range query in the active database. |
| 204 | | * |
| 205 | | * Use this as a substitute for db_query() when a subset of the query is to be |
| 206 | | * returned. |
| 207 | | * User-supplied arguments to the query should be passed in as separate parameters |
| 208 | | * so that they can be properly escaped to avoid SQL injection attacks. |
| 209 | | * |
| 210 | | * @param $query |
| 211 | | * A string containing an SQL query. |
| 212 | | * @param ... |
| 213 | | * A variable number of arguments which are substituted into the query |
| 214 | | * using printf() syntax. The query arguments can be enclosed in one |
| 215 | | * array instead. |
| 216 | | * Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose |
| 217 | | * in '') and %%. |
| 218 | | * |
| 219 | | * NOTE: using this syntax will cast NULL and FALSE values to decimal 0, |
| 220 | | * and TRUE values to decimal 1. |
| 221 | | * |
| 222 | | * @param $from |
| 223 | | * The first result row to return. |
| 224 | | * @param $count |
| 225 | | * The maximum number of result rows to return. |
| 226 | | * @return |
| 227 | | * A database query result resource, or FALSE if the query was not executed |
| 228 | | * correctly. |
| 229 | | */ |
| 230 | | function db_query_range($query) { |
| 231 | 1 | $args = func_get_args(); |
| 232 | 1 | $count = array_pop($args); |
| 233 | 1 | $from = array_pop($args); |
| 234 | 1 | array_shift($args); |
| 235 | | |
| 236 | 1 | $query = db_prefix_tables($query); |
| 237 | 1 | if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax |
| 238 | 1 | $args = $args[0]; |
| 239 | | } |
| 240 | 1 | _db_query_callback($args, TRUE); |
| 241 | 1 | $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query); |
| 242 | 1 | $query .= ' LIMIT '. (int)$from .', '. (int)$count; |
| 243 | 1 | return _db_query($query); |
| 244 | | } |
| 245 | | |
| 246 | | /** |
| 247 | | * Runs a SELECT query and stores its results in a temporary table. |
| 248 | | * |
| 249 | | * Use this as a substitute for db_query() when the results need to stored |
| 250 | | * in a temporary table. Temporary tables exist for the duration of the page |
| 251 | | * request. |
| 252 | | * User-supplied arguments to the query should be passed in as separate parameters |
| 253 | | * so that they can be properly escaped to avoid SQL injection attacks. |
| 254 | | * |
| 255 | | * Note that if you need to know how many results were returned, you should do |
| 256 | | * a SELECT COUNT(*) on the temporary table afterwards. db_affected_rows() does |
| 257 | | * not give consistent result across different database types in this case. |
| 258 | | * |
| 259 | | * @param $query |
| 260 | | * A string containing a normal SELECT SQL query. |
| 261 | | * @param ... |
| 262 | | * A variable number of arguments which are substituted into the query |
| 263 | | * using printf() syntax. The query arguments can be enclosed in one |
| 264 | | * array instead. |
| 265 | | * Valid %-modifiers are: %s, %d, %f, %b (binary data, do not enclose |
| 266 | | * in '') and %%. |
| 267 | | * |
| 268 | | * NOTE: using this syntax will cast NULL and FALSE values to decimal 0, |
| 269 | | * and TRUE values to decimal 1. |
| 270 | | * |
| 271 | | * @param $table |
| 272 | | * The name of the temporary table to select into. This name will not be |
| 273 | | * prefixed as there is no risk of collision. |
| 274 | | * @return |
| 275 | | * A database query result resource, or FALSE if the query was not executed |
| 276 | | * correctly. |
| 277 | | */ |
| 278 | | function db_query_temporary($query) { |
| 279 | | $args = func_get_args(); |
| 280 | | $tablename = array_pop($args); |
| 281 | | array_shift($args); |
| 282 | | |
| 283 | | $query = preg_replace('/^SELECT/i', 'CREATE TEMPORARY TABLE '. $tablename .' Engine=HEAP SELECT', db_prefix_tables($query)); |
| 284 | | if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax |
| 285 | | $args = $args[0]; |
| 286 | | } |
| 287 | | _db_query_callback($args, TRUE); |
| 288 | | $query = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $query); |
| 289 | | return _db_query($query); |
| 290 | | } |
| 291 | | |
| 292 | | /** |
| 293 | | * Returns a properly formatted Binary Large OBject value. |
| 294 | | * |
| 295 | | * @param $data |
| 296 | | * Data to encode. |
| 297 | | * @return |
| 298 | | * Encoded data. |
| 299 | | */ |
| 300 | | function db_encode_blob($data) { |
| 301 | 1 | global $active_db; |
| 302 | 1 | return "'". mysql_real_escape_string($data, $active_db) ."'"; |
| 303 | | } |
| 304 | | |
| 305 | | /** |
| 306 | | * Returns text from a Binary Large Object value. |
| 307 | | * |
| 308 | | * @param $data |
| 309 | | * Data to decode. |
| 310 | | * @return |
| 311 | | * Decoded data. |
| 312 | | */ |
| 313 | | function db_decode_blob($data) { |
| 314 | 1 | return $data; |
| 315 | | } |
| 316 | | |
| 317 | | /** |
| 318 | | * Prepare user input for use in a database query, preventing SQL injection attacks. |
| 319 | | */ |
| 320 | | function db_escape_string($text) { |
| 321 | 1 | global $active_db; |
| 322 | 1 | return mysql_real_escape_string($text, $active_db); |
| 323 | | } |
| 324 | | |
| 325 | | /** |
| 326 | | * Lock a table. |
| 327 | | */ |
| 328 | | function db_lock_table($table) { |
| 329 | | db_query('LOCK TABLES {'. db_escape_table($table) .'} WRITE'); |
| 330 | | } |
| 331 | | |
| 332 | | /** |
| 333 | | * Unlock all locked tables. |
| 334 | | */ |
| 335 | | function db_unlock_tables() { |
| 336 | | db_query('UNLOCK TABLES'); |
| 337 | | } |
| 338 | | |
| 339 | | /** |
| 340 | | * Check if a table exists. |
| 341 | | */ |
| 342 | | function db_table_exists($table) { |
| 343 | | return (bool) db_fetch_object(db_query("SHOW TABLES LIKE '{". db_escape_table($table) ."}'")); |
| 344 | | } |
| 345 | | |
| 346 | | /** |
| 347 | | * Check if a column exists in the given table. |
| 348 | | */ |
| 349 | | function db_column_exists($table, $column) { |
| 350 | | return (bool) db_fetch_object(db_query("SHOW COLUMNS FROM {". db_escape_table($table) ."} LIKE '". db_escape_table($column) ."'")); |
| 351 | | } |
| 352 | | |
| 353 | | /** |
| 354 | | * Wraps the given table.field entry with a DISTINCT(). The wrapper is added to |
| 355 | | * the SELECT list entry of the given query and the resulting query is returned. |
| 356 | | * This function only applies the wrapper if a DISTINCT doesn't already exist in |
| 357 | | * the query. |
| 358 | | * |
| 359 | | * @param $table Table containing the field to set as DISTINCT |
| 360 | | * @param $field Field to set as DISTINCT |
| 361 | | * @param $query Query to apply the wrapper to |
| 362 | | * @return SQL query with the DISTINCT wrapper surrounding the given table.field. |
| 363 | | */ |
| 364 | | function db_distinct_field($table, $field, $query) { |
| 365 | | $field_to_select = 'DISTINCT('. $table .'.'. $field .')'; |
| 366 | | // (?<!text) is a negative look-behind (no need to rewrite queries that already use DISTINCT). |
| 367 | | return preg_replace('/(SELECT.*)(?:'. $table .'\.|\s)(?<!DISTINCT\()(?<!DISTINCT\('. $table .'\.)'. $field .'(.*FROM )/AUsi', '\1 '. $field_to_select .'\2', $query); |
| 368 | | } |
| 369 | | |
| 370 | | /** |
| 371 | | * @} End of "ingroup database". |
| 372 | | */ |