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 | | */ |