root/ip/trunk/install/includes/mysql.php
| 181 | 235 | ||
|---|---|---|---|
15 | * | 15 | * |
16 | */ | 16 | */ |
17 | 17 | ||
18 | if(!defined('SQL_LAYER')) | 18 | if (!defined('IN_ICYPHOENIX')) |
19 | { | 19 | { |
20 | exit; | ||
21 | } | ||
20 | 22 | ||
21 | define('SQL_LAYER', 'mysql'); | ||
22 | |||
23 | class sql_db | 23 | class sql_db |
24 | { | 24 | { |
25 | |||
26 | var $db_connect_id; | 25 | var $db_connect_id; |
26 | var $query_string = ''; | ||
27 | var $query_result; | 27 | var $query_result; |
28 | var $row = array(); | 28 | var $row = array(); |
29 | var $rowset = array(); | 29 | var $rowset = array(); |
30 | var $num_queries = 0; | 30 | var $num_queries = array(); |
31 | var $open_queries = array(); | ||
32 | var $transaction = false; | ||
33 | var $transactions = 0; | ||
34 | var $persistency = false; | ||
35 | var $multi_insert = false; | ||
31 | 36 | ||
32 | // | 37 | var $cache_folder = ''; |
38 | |||
39 | var $curtime = 0; | ||
40 | var $sql_time = 0; | ||
41 | var $sql_init_time = 0; | ||
42 | var $sql_start_time = 0; | ||
43 | var $sql_end_time = 0; | ||
44 | |||
45 | var $query_hold = ''; | ||
46 | var $html_hold = ''; | ||
47 | var $sql_report = ''; | ||
48 | |||
49 | var $return_on_error = false; | ||
50 | var $sql_error_triggered = false; // Set to true if error triggered | ||
51 | var $sql_error_sql = ''; // Holding the last sql query on sql error | ||
52 | var $sql_error_returned = array(); // Holding the error information - only populated if sql_error_triggered is set | ||
53 | |||
54 | /** | ||
55 | * Exact version of the DBAL, directly queried | ||
56 | */ | ||
57 | var $sql_server_version = false; | ||
58 | |||
59 | /** | ||
60 | * Wildcards for matching any (%) or exactly one (_) character within LIKE expressions | ||
61 | */ | ||
62 | var $any_char; | ||
63 | var $one_char; | ||
64 | |||
33 | // Constructor | 65 | // Constructor |
34 | // | ||
35 | function sql_db($sqlserver, $sqluser, $sqlpassword, $database, $persistency = true) | 66 | function sql_db($sqlserver, $sqluser, $sqlpassword, $database, $persistency = true) |
36 | { | 67 | { |
68 | $this->sql_start_time = $this->sql_get_time(); | ||
69 | $this->sql_init_time = $this->sql_start_time; | ||
37 | 70 | ||
71 | $this->num_queries = array( | ||
72 | 'cached' => 0, | ||
73 | 'normal' => 0, | ||
74 | 'total' => 0, | ||
75 | ); | ||
76 | |||
77 | // Fill default sql layer based on the class being called. | ||
78 | // This can be changed by the specified layer itself later if needed. | ||
79 | $this->sql_layer = SQL_LAYER; | ||
80 | |||
81 | // Do not change this please! This variable is used to easy the use of it - and is hardcoded. | ||
82 | $this->any_char = chr(0) . '%'; | ||
83 | $this->one_char = chr(0) . '_'; | ||
84 | |||
38 | $this->persistency = $persistency; | 85 | $this->persistency = $persistency; |
39 | $this->user = $sqluser; | 86 | $this->user = $sqluser; |
40 | $this->password = $sqlpassword; | 87 | $this->password = $sqlpassword; |
41 | $this->server = $sqlserver; | 88 | $this->server = $sqlserver; |
42 | $this->dbname = $database; | 89 | $this->dbname = $database; |
43 | 90 | ||
44 | if($this->persistency) | 91 | $this->db_connect_id = ($this->persistency) ? @mysql_pconnect($this->server, $this->user, $this->password) : @mysql_connect($this->server, $this->user, $this->password); |
45 | { | 92 | |
46 | $this->db_connect_id = @mysql_pconnect($this->server, $this->user, $this->password); | ||
47 | } | ||
48 | else | ||
49 | { | ||
50 | $this->db_connect_id = @mysql_connect($this->server, $this->user, $this->password); | ||
51 | } | ||
52 | if($this->db_connect_id) | 93 | if($this->db_connect_id) |
53 | { | 94 | { |
54 | if($database != "") | 95 | if($database != '') |
55 | { | 96 | { |
56 | $this->dbname = $database; | 97 | $this->dbname = $database; |
57 | $dbselect = @mysql_select_db($this->dbname); | 98 | $dbselect = @mysql_select_db($this->dbname); |
99 | |||
58 | if(!$dbselect) | 100 | if(!$dbselect) |
59 | { | 101 | { |
60 | @mysql_close($this->db_connect_id); | 102 | @mysql_close($this->db_connect_id); |
61 | $this->db_connect_id = $dbselect; | 103 | $this->db_connect_id = $dbselect; |
62 | } | 104 | } |
63 | } | 105 | } |
64 | return $this->db_connect_id; | 106 | |
107 | $result = $this->db_connect_id; | ||
65 | } | 108 | } |
66 | else | 109 | else |
67 | { | 110 | { |
68 | return false; | 111 | $this->sql_error(''); |
112 | $result = false; | ||
69 | } | 113 | } |
114 | |||
115 | // make db connection UTF-8 aware | ||
116 | if ($this->db_connect_id) | ||
117 | { | ||
118 | @mysql_query("SET NAMES 'utf8'"); | ||
119 | } | ||
120 | |||
121 | $this->sql_server_version = $this->sql_server_info(true); | ||
122 | |||
123 | $this->sql_end_time = $this->sql_get_time(); | ||
124 | $this->sql_time += $this->sql_end_time - $this->sql_start_time; | ||
125 | |||
126 | return $result; | ||
70 | } | 127 | } |
71 | 128 | ||
72 | // | 129 | /** |
73 | // Other base methods | 130 | * Version information about used database |
74 | // | 131 | * @param bool $raw if true, only return the fetched sql_server_version |
132 | * @return string sql server version | ||
133 | */ | ||
134 | function sql_server_info($raw = false) | ||
135 | { | ||
136 | global $cache; | ||
137 | |||
138 | if (empty($cache) || ($this->sql_server_version = $cache->get('mysql_version')) === false) | ||
139 | { | ||
140 | $result = @mysql_query('SELECT VERSION() AS version', $this->db_connect_id); | ||
141 | $row = @mysql_fetch_assoc($result); | ||
142 | @mysql_free_result($result); | ||
143 | |||
144 | $this->sql_server_version = $row['version']; | ||
145 | |||
146 | if (!empty($cache)) | ||
147 | { | ||
148 | $cache->put('mysql_version', $this->sql_server_version); | ||
149 | } | ||
150 | } | ||
151 | |||
152 | return ($raw) ? $this->sql_server_version : 'MySQL ' . $this->sql_server_version; | ||
153 | } | ||
154 | |||
155 | /** | ||
156 | * Get microtime | ||
157 | */ | ||
158 | function sql_get_time() | ||
159 | { | ||
160 | $mtime = explode(' ', microtime()); | ||
161 | return $mtime[1] + $mtime[0]; | ||
162 | } | ||
163 | |||
164 | /** | ||
165 | * Close DB connection | ||
166 | */ | ||
75 | function sql_close() | 167 | function sql_close() |
76 | { | 168 | { |
169 | $this->sql_start_time = $this->sql_get_time(); | ||
170 | |||
77 | if($this->db_connect_id) | 171 | if($this->db_connect_id) |
78 | { | 172 | { |
79 | if($this->query_result) | 173 | // Commit any remaining transactions |
174 | if($this->transaction) | ||
80 | { | 175 | { |
81 | @mysql_free_result($this->query_result); | 176 | do |
177 | { | ||
178 | $this->sql_transaction('commit'); | ||
179 | } | ||
180 | while ($this->transaction); | ||
82 | } | 181 | } |
83 | $result = @mysql_close($this->db_connect_id); | 182 | |
84 | return $result; | 183 | foreach ($this->open_queries as $query_id) |
184 | { | ||
185 | $this->sql_freeresult($query_id); | ||
186 | } | ||
187 | |||
188 | // Connection closed correctly. Set db_connect_id to false to prevent errors | ||
189 | if ($result = $this->_sql_close()) | ||
190 | { | ||
191 | $this->db_connect_id = false; | ||
192 | } | ||
85 | } | 193 | } |
86 | else | 194 | else |
87 | { | 195 | { |
196 | $result = false; | ||
197 | } | ||
198 | |||
199 | $this->sql_end_time = $this->sql_get_time(); | ||
200 | $this->sql_time += $this->sql_end_time - $this->sql_start_time; | ||
201 | |||
202 | return $result; | ||
203 | } | ||
204 | |||
205 | /** | ||
206 | * Return number of sql queries and cached sql queries used | ||
207 | */ | ||
208 | function sql_num_queries($cached = false) | ||
209 | { | ||
210 | return ($cached) ? $this->num_queries['cached'] : $this->num_queries['normal']; | ||
211 | } | ||
212 | |||
213 | /** | ||
214 | * Add to query count | ||
215 | */ | ||
216 | function sql_add_num_queries($cached = false) | ||
217 | { | ||
218 | $this->num_queries['cached'] += ($cached !== false) ? 1 : 0; | ||
219 | $this->num_queries['normal'] += ($cached !== false) ? 0 : 1; | ||
220 | $this->num_queries['total'] += 1; | ||
221 | } | ||
222 | |||
223 | /** | ||
224 | * Build LIMIT query | ||
225 | * Doing some validation here. | ||
226 | */ | ||
227 | function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) | ||
228 | { | ||
229 | if (empty($query)) | ||
230 | { | ||
88 | return false; | 231 | return false; |
89 | } | 232 | } |
233 | |||
234 | // Never use a negative total or offset | ||
235 | $total = ($total < 0) ? 0 : $total; | ||
236 | $offset = ($offset < 0) ? 0 : $offset; | ||
237 | |||
238 | return $this->_sql_query_limit($query, $total, $offset, $cache_ttl); | ||
90 | } | 239 | } |
91 | 240 | ||
92 | // | 241 | /** |
93 | // Base query method | 242 | * Base query method |
94 | // | 243 | */ |
95 | function sql_query($query = "", $transaction = FALSE) | 244 | function sql_query($query = '', $cache_ttl = 0, $cache_prefix = false, $cache_folder = SQL_CACHE_FOLDER) |
96 | { | 245 | { |
97 | // Remove any pre-existing queries | 246 | if (empty($query)) |
98 | unset($this->query_result); | ||
99 | if($query != "") | ||
100 | { | 247 | { |
101 | $this->num_queries++; | 248 | return false; |
249 | } | ||
102 | 250 | ||
103 | $this->query_result = @mysql_query($query, $this->db_connect_id); | 251 | global $cache; |
252 | |||
253 | $this->sql_start_time = $this->sql_get_time(); | ||
254 | |||
255 | $cache_folder = (empty($cache_folder) ? SQL_CACHE_FOLDER : $cache_folder); | ||
256 | |||
257 | if (defined('DEBUG_EXTRA')) | ||
258 | { | ||
259 | $this->sql_report('start', $query); | ||
104 | } | 260 | } |
105 | if($this->query_result) | 261 | |
262 | if (CACHE_SQL == false) | ||
106 | { | 263 | { |
107 | unset($this->row[$this->query_result]); | 264 | $cache_prefix = false; |
108 | unset($this->rowset[$this->query_result]); | ||
109 | return $this->query_result; | ||
110 | } | 265 | } |
111 | else | 266 | |
267 | $cache_ttl = empty($cache_prefix) ? 0 : (empty($cache_ttl) ? CACHE_SQL_EXPIRY : $cache_ttl); | ||
268 | |||
269 | // Cache SQL to the same file plus underscore | ||
270 | if (defined('SQL_DEBUG_LOG') && SQL_DEBUG_LOG && !defined('IN_ADMIN')) | ||
112 | { | 271 | { |
113 | return ( $transaction == END_TRANSACTION ) ? true : false; | 272 | $f = fopen($this->cache_folder . 'sql_history.' . PHP_EXT, 'a+'); |
273 | @flock($f, LOCK_EX); | ||
274 | @fwrite($f, gmdate('Y/m/d - H:i:s') . ' => ' . $hash . "\n\n" . $query . "\n\n\n=========================\n\n"); | ||
275 | @flock($f, LOCK_UN); | ||
276 | @fclose($f); | ||
114 | } | 277 | } |
278 | |||
279 | $this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query, $cache_prefix, $cache_folder) : false; | ||
280 | $this->sql_add_num_queries($this->query_result); | ||
281 | |||
282 | if ($this->query_result === false) | ||
283 | { | ||
284 | if ((($this->query_result = @mysql_query($query, $this->db_connect_id)) === false) && !defined('IN_INSTALL')) | ||
285 | { | ||
286 | $this->sql_end_time = $this->sql_get_time(); | ||
287 | $this->sql_time += $this->sql_end_time - $this->sql_start_time; | ||
288 | |||
289 | $this->sql_error($query); | ||
290 | } | ||
291 | |||
292 | if (defined('DEBUG_EXTRA')) | ||
293 | { | ||
294 | $this->sql_end_time = $this->sql_get_time(); | ||
295 | $this->sql_time += $this->sql_end_time - $this->sql_start_time; | ||
296 | |||
297 | $this->sql_report('stop', $query); | ||
298 | } | ||
299 | |||
300 | if ($cache_ttl && method_exists($cache, 'sql_save')) | ||
301 | { | ||
302 | $this->open_queries[(int) $this->query_result] = $this->query_result; | ||
303 | $cache->sql_save($query, $this->query_result, $cache_ttl, $cache_prefix, $cache_folder); | ||
304 | } | ||
305 | elseif (strpos($query, 'SELECT') === 0 && $this->query_result) | ||
306 | { | ||
307 | $this->open_queries[(int) $this->query_result] = $this->query_result; | ||
308 | } | ||
309 | } | ||
310 | elseif (defined('DEBUG_EXTRA')) | ||
311 | { | ||
312 | $this->sql_report('fromcache', $query); | ||
313 | } | ||
314 | |||
315 | $this->sql_end_time = $this->sql_get_time(); | ||
316 | $this->sql_time += $this->sql_end_time - $this->sql_start_time; | ||
317 | |||
318 | return $this->query_result; | ||
115 | } | 319 | } |
116 | 320 | ||
117 | // | 321 | /** |
118 | // Other query methods | 322 | * Get numrows |
119 | // | 323 | */ |
120 | function sql_numrows($query_id = 0) | 324 | function sql_numrows($query_id = 0) |
121 | { | 325 | { |
122 | if(!$query_id) | 326 | if(!$query_id) |
123 | { | 327 | { |
124 | $query_id = $this->query_result; | 328 | $query_id = $this->query_result; |
125 | } | 329 | } |
126 | if($query_id) | 330 | |
127 | { | 331 | return ($query_id) ? @mysql_num_rows($query_id) : false; |
128 | $result = @mysql_num_rows($query_id); | ||
129 | return $result; | ||
130 | } | ||
131 | else | ||
132 | { | ||
133 | return false; | ||
134 | } | ||
135 | } | 332 | } |
136 | 333 | ||
334 | /** | ||
335 | * Get affected rows | ||
336 | */ | ||
137 | function sql_affectedrows() | 337 | function sql_affectedrows() |
138 | { | 338 | { |
139 | if($this->db_connect_id) | 339 | return ($this->db_connect_id) ? @mysql_affected_rows($this->db_connect_id) : false; |
340 | } | ||
341 | |||
342 | /** | ||
343 | * Fetch current row | ||
344 | */ | ||
345 | function sql_fetchrow($query_id = false) | ||
346 | { | ||
347 | global $cache; | ||
348 | |||
349 | if ($query_id === false) | ||
140 | { | 350 | { |
141 | $result = @mysql_affected_rows($this->db_connect_id); | 351 | $query_id = $this->query_result; |
142 | return $result; | ||
143 | } | 352 | } |
144 | else | 353 | |
354 | if (isset($cache->sql_rowset[$query_id])) | ||
145 | { | 355 | { |
146 | return false; | 356 | return $cache->sql_fetchrow($query_id); |
147 | } | 357 | } |
358 | |||
359 | return ($query_id !== false) ? @mysql_fetch_assoc($query_id) : false; | ||
148 | } | 360 | } |
149 | 361 | ||
150 | function sql_numfields($query_id = 0) | 362 | /** |
363 | * Fetch all rows | ||
364 | */ | ||
365 | function sql_fetchrowset($query_id = false) | ||
151 | { | 366 | { |
152 | if(!$query_id) | 367 | $result = false; |
368 | if ($query_id === false) | ||
153 | { | 369 | { |
154 | $query_id = $this->query_result; | 370 | $query_id = $this->query_result; |
155 | } | 371 | } |
156 | if($query_id) | 372 | |
373 | if ($query_id !== false) | ||
157 | { | 374 | { |
158 | $result = @mysql_num_fields($query_id); | 375 | $result = array(); |
376 | while ($row = $this->sql_fetchrow($query_id)) | ||
377 | { | ||
378 | $result[] = $row; | ||
379 | } | ||
380 | |||
159 | return $result; | 381 | return $result; |
160 | } | 382 | } |
161 | else | 383 | |
162 | { | 384 | return false; |
163 | return false; | ||
164 | } | ||
165 | } | 385 | } |
166 | 386 | ||
167 | function sql_fieldname($offset, $query_id = 0) | 387 | /** |
388 | * Fetch field | ||
389 | * if rownum is false, the current row is used, else it is pointing to the row (zero-based) | ||
390 | */ | ||
391 | function sql_fetchfield($field, $rownum = false, $query_id = false) | ||
168 | { | 392 | { |
169 | if(!$query_id) | 393 | global $cache; |
394 | |||
395 | $result = false; | ||
396 | if ($query_id === false) | ||
170 | { | 397 | { |
171 | $query_id = $this->query_result; | 398 | $query_id = $this->query_result; |
172 | } | 399 | } |
173 | if($query_id) | 400 | |
401 | if ($query_id !== false) | ||
174 | { | 402 | { |
175 | $result = @mysql_field_name($query_id, $offset); | 403 | if ($rownum !== false) |
176 | return $result; | 404 | { |
405 | $this->sql_rowseek($rownum, $query_id); | ||
406 | } | ||
407 | |||
408 | if (!is_object($query_id) && isset($cache->sql_rowset[$query_id])) | ||
409 | { | ||
410 | return $cache->sql_fetchfield($query_id, $field); | ||
411 | } | ||
412 | |||
413 | $row = $this->sql_fetchrow($query_id); | ||
414 | $result = (isset($row[$field])) ? $row[$field] : false; | ||
177 | } | 415 | } |
178 | else | 416 | |
179 | { | 417 | return $result; |
180 | return false; | ||
181 | } | ||
182 | } | 418 | } |
183 | 419 | ||
184 | function sql_fieldtype($offset, $query_id = 0) | 420 | /** |
421 | * Get num fields | ||
422 | */ | ||
423 | function sql_numfields($query_id = 0) | ||
185 | { | 424 | { |
186 | if(!$query_id) | 425 | if(!$query_id) |
187 | { | 426 | { |
188 | $query_id = $this->query_result; | 427 | $query_id = $this->query_result; |
189 | } | 428 | } |
190 | if($query_id) | 429 | |
191 | { | 430 | return ($query_id) ? @mysql_num_fields($query_id) : false; |
192 | $result = @mysql_field_type($query_id, $offset); | ||
193 | return $result; | ||
194 | } | ||
195 | else | ||
196 | { | ||
197 | return false; | ||
198 | } | ||
199 | } | 431 | } |
200 | 432 | ||
201 | function sql_fetchrow($query_id = 0) | 433 | /** |
434 | * Get field name | ||
435 | */ | ||
436 | function sql_fieldname($offset, $query_id = 0) | ||
202 | { | 437 | { |
203 | if(!$query_id) | 438 | if(!$query_id) |
204 | { | 439 | { |
205 | $query_id = $this->query_result; | 440 | $query_id = $this->query_result; |
206 | } | 441 | } |
207 | if($query_id) | 442 | |
208 | { | 443 | return ($query_id) ? @mysql_field_name($query_id, $offset) : false; |
209 | $this->row[$query_id] = @mysql_fetch_array($query_id); | ||
210 | return $this->row[$query_id]; | ||
211 | } | ||
212 | else | ||
213 | { | ||
214 | return false; | ||
215 | } | ||
216 | } | 444 | } |
217 | 445 | ||
218 | function sql_fetchrowset($query_id = 0) | 446 | /** |
447 | * Get field type | ||
448 | */ | ||
449 | function sql_fieldtype($offset, $query_id = 0) | ||
219 | { | 450 | { |
220 | if(!$query_id) | 451 | if(!$query_id) |
221 | { | 452 | { |
222 | $query_id = $this->query_result; | 453 | $query_id = $this->query_result; |
223 | } | 454 | } |
224 | if($query_id) | 455 | |
225 | { | 456 | return ($query_id) ? @mysql_field_type($query_id, $offset) : false; |
226 | unset($this->rowset[$query_id]); | ||
227 | unset($this->row[$query_id]); | ||
228 | while($this->rowset[$query_id] = @mysql_fetch_array($query_id)) | ||
229 | { | ||
230 | $result[] = $this->rowset[$query_id]; | ||
231 | } | ||
232 | return $result; | ||
233 | } | ||
234 | else | ||
235 | { | ||
236 | return false; | ||
237 | } | ||
238 | } | 457 | } |
239 | 458 | ||
240 | function sql_fetchfield($field, $rownum = -1, $query_id = 0) | 459 | /** |
460 | * Seek to given row number | ||
461 | * rownum is zero-based | ||
462 | */ | ||
463 | function sql_rowseek($rownum, &$query_id) | ||
241 | { | 464 | { |
242 | if(!$query_id) | 465 | global $cache; |
466 | |||
467 | if ($query_id === false) | ||
243 | { | 468 | { |
244 | $query_id = $this->query_result; | 469 | $query_id = $this->query_result; |
245 | } | 470 | } |
246 | if($query_id) | 471 | |
472 | if (isset($cache->sql_rowset[$query_id])) | ||
247 | { | 473 | { |
248 | if($rownum > -1) | 474 | return $cache->sql_rowseek($rownum, $query_id); |
249 | { | ||
250 | $result = @mysql_result($query_id, $rownum, $field); | ||
251 | } | ||
252 | else | ||
253 | { | ||
254 | if(empty($this->row[$query_id]) && empty($this->rowset[$query_id])) | ||
255 | { | ||
256 | if($this->sql_fetchrow()) | ||
257 | { | ||
258 | $result = $this->row[$query_id][$field]; | ||
259 | } | ||
260 | } | ||
261 | else | ||
262 | { | ||
263 | if($this->rowset[$query_id]) | ||
264 | { | ||
265 | $result = $this->rowset[$query_id][0][$field]; | ||
266 | } | ||
267 | else if($this->row[$query_id]) | ||
268 | { | ||
269 | $result = $this->row[$query_id][$field]; | ||
270 | } | ||
271 | } | ||
272 | } | ||
273 | return $result; | ||
274 | } | 475 | } |
275 | else | 476 | |
276 | { | 477 | return ($query_id !== false) ? @mysql_data_seek($query_id, $rownum) : false; |
277 | return false; | ||
278 | } | ||
279 | } | 478 | } |
280 | 479 | ||
281 | /** | 480 | /** |
481 | * Get last inserted id after insert statement | ||
482 | */ | ||
483 | function sql_nextid() | ||
484 | { | ||
485 | return ($this->db_connect_id) ? @mysql_insert_id($this->db_connect_id) : false; | ||
486 | } | ||
487 | |||
488 | /** | ||
282 | * Function for validating values | 489 | * Function for validating values |
283 | */ | 490 | */ |
284 | function sql_validate_value($var) | 491 | function sql_validate_value($var) |
... | ... | ||
311 | } | 518 | } |
312 | 519 | ||
313 | /** | 520 | /** |
521 | * Correctly adjust LIKE expression for special characters | ||
522 | * Some DBMS are handling them in a different way | ||
523 | * | ||
524 | * @param string $expression The expression to use. Every wildcard is escaped, except $this->any_char and $this->one_char | ||
525 | * @return string LIKE expression including the keyword! | ||
526 | */ | ||
527 | function sql_like_expression($expression) | ||
528 | { | ||
529 | $expression = str_replace(array('_', '%'), array("\_", "\%"), $expression); | ||
530 | $expression = str_replace(array(chr(0) . "\_", chr(0) . "\%"), array('_', '%'), $expression); | ||
531 | |||
532 | return $this->_sql_like_expression('LIKE \'' . $this->sql_escape($expression) . '\''); | ||
533 | } | ||
534 | |||
535 | /** | ||
536 | * Build sql statement from array for select and select distinct statements | ||
537 | * | ||
538 | * Possible query values: SELECT, SELECT_DISTINCT | ||
539 | */ | ||
540 | function sql_build_query($query, $array) | ||
541 | { | ||
542 | $sql = ''; | ||
543 | switch ($query) | ||
544 | { | ||
545 | case 'SELECT': | ||
546 | case 'SELECT_DISTINCT'; | ||
547 | |||
548 | $sql = str_replace('_', ' ', $query) . ' ' . $array['SELECT'] . ' FROM '; | ||
549 | |||
550 | // Build table array. We also build an alias array for later checks. | ||
551 | $table_array = $aliases = array(); | ||
552 | $used_multi_alias = false; | ||
553 | |||
554 | foreach ($array['FROM'] as $table_name => $alias) | ||
555 | { | ||
556 | if (is_array($alias)) | ||
557 | { | ||
558 | $used_multi_alias = true; | ||
559 | |||
560 | foreach ($alias as $multi_alias) | ||
561 | { | ||
562 | $table_array[] = $table_name . ' ' . $multi_alias; | ||
563 | $aliases[] = $multi_alias; | ||
564 | } | ||
565 | } | ||
566 | else | ||
567 | { | ||
568 | $table_array[] = $table_name . ' ' . $alias; | ||
569 | $aliases[] = $alias; | ||
570 | } | ||
571 | } | ||
572 | |||
573 | // We run the following code to determine if we need to re-order the table array. ;) | ||
574 | // The reason for this is that for multi-aliased tables (two equal tables) in the FROM statement the last table need to match the first comparison. | ||
575 | // DBMS who rely on this: Oracle, PostgreSQL and MSSQL. For all other DBMS it makes absolutely no difference in which order the table is. | ||
576 | if (!empty($array['LEFT_JOIN']) && sizeof($array['FROM']) > 1 && $used_multi_alias !== false) | ||
577 | { | ||
578 | // Take first LEFT JOIN | ||
579 | $join = current($array['LEFT_JOIN']); | ||
580 | |||
581 | // Determine the table used there (even if there are more than one used, we only want to have one | ||
582 | preg_match('/(' . implode('|', $aliases) . ')\.[^\s]+/U', str_replace(array('(', ')', 'AND', 'OR', ' '), '', $join['ON']), $matches); | ||
583 | |||
584 | // If there is a first join match, we need to make sure the table order is correct | ||
585 | if (!empty($matches[1])) | ||
586 | { | ||
587 | $first_join_match = trim($matches[1]); | ||
588 | $table_array = $last = array(); | ||
589 | |||
590 | foreach ($array['FROM'] as $table_name => $alias) | ||
591 | { | ||
592 | if (is_array($alias)) | ||
593 | { | ||
594 | foreach ($alias as $multi_alias) | ||
595 | { | ||
596 | ($multi_alias === $first_join_match) ? $last[] = $table_name . ' ' . $multi_alias : $table_array[] = $table_name . ' ' . $multi_alias; | ||
597 | } | ||
598 | } | ||
599 | else | ||
600 | { | ||
601 | ($alias === $first_join_match) ? $last[] = $table_name . ' ' . $alias : $table_array[] = $table_name . ' ' . $alias; | ||
602 | } | ||
603 | } | ||
604 | |||
605 | $table_array = array_merge($table_array, $last); | ||
606 | } | ||
607 | } | ||
608 | |||
609 | $sql .= $this->_sql_custom_build('FROM', implode(', ', $table_array)); | ||
610 | |||
611 | if (!empty($array['LEFT_JOIN'])) | ||
612 | { | ||
613 | foreach ($array['LEFT_JOIN'] as $join) | ||
614 | { | ||
615 | $sql .= ' LEFT JOIN ' . key($join['FROM']) . ' ' . current($join['FROM']) . ' ON (' . $join['ON'] . ')'; | ||
616 | } | ||
617 | } | ||
618 | |||
619 | if (!empty($array['WHERE'])) | ||
620 | { | ||
621 | $sql .= ' WHERE ' . $this->_sql_custom_build('WHERE', $array['WHERE']); | ||
622 | } | ||
623 | |||
624 | if (!empty($array['GROUP_BY'])) | ||
625 | { | ||
626 | $sql .= ' GROUP BY ' . $array['GROUP_BY']; | ||
627 | } | ||
628 | |||
629 | if (!empty($array['ORDER_BY'])) | ||
630 | { | ||
631 | $sql .= ' ORDER BY ' . $array['ORDER_BY']; | ||
632 | } | ||
633 | |||
634 | break; | ||
635 | } | ||
636 | |||
637 | return $sql; | ||
638 | } | ||
639 | |||
640 | /** | ||
641 | * Build SQL to INSERT or UPDATE from the provided array | ||
642 | */ | ||
643 | function sql_build_insert_update($sql_input_array, $sql_insert = true) | ||
644 | { | ||
645 | $insert_fields_sql = ''; | ||
646 | $insert_values_sql = ''; | ||
647 | $update_sql = ''; | ||
648 | foreach ($sql_input_array as $k => $v) | ||
649 | { | ||
650 | $insert_fields_sql .= (($insert_fields_sql == '') ? '' : ', ') . $k; | ||
651 | $insert_values_sql .= (($insert_values_sql == '') ? '' : ', ') . $this->sql_validate_value($v); | ||
652 | $update_sql .= (($update_sql == '') ? '' : ', ') . $k . ' = ' . $this->sql_validate_value($v); | ||
653 | } | ||
654 | |||
655 | $sql_string = $sql_insert ? (' (' . $insert_fields_sql . ') VALUES (' . $insert_values_sql . ')') : $update_sql; | ||
656 | |||
657 | return $sql_string; | ||
658 | } | ||
659 | |||
660 | /** | ||
314 | * Build sql statement from array for insert/update/select statements | 661 | * Build sql statement from array for insert/update/select statements |
315 | * | 662 | * |
316 | * Idea for this from Ikonboard | 663 | * Idea for this from Ikonboard |
... | ... | ||
362 | return $query; | 709 | return $query; |
363 | } | 710 | } |
364 | 711 | ||
365 | function sql_rowseek($rownum, $query_id = 0){ | 712 | /** |
366 | if(!$query_id) | 713 | * Build IN or NOT IN sql comparison string, uses <> or = on single element arrays to improve comparison speed |
714 | * | ||
715 | * @access public | ||
716 | * @param string $field name of the sql column that shall be compared | ||
717 | * @param array $array array of values that are allowed (IN) or not allowed (NOT IN) | ||
718 | * @param bool $negate true for NOT IN (), false for IN () (default) | ||
719 | * @param bool $allow_empty_set If true, allow $array to be empty, this function will return 1=1 or 1=0 then. Default to false. | ||
720 | */ | ||
721 | function sql_in_set($field, $array, $negate = false, $allow_empty_set = false) | ||
722 | { | ||
723 | if (!sizeof($array)) | ||
367 | { | 724 | { |
368 | $query_id = $this->query_result; | 725 | if (!$allow_empty_set) |
726 | { | ||
727 | // Print the backtrace to help identifying the location of the problematic code | ||
728 | $this->sql_error('No values specified for SQL IN comparison'); | ||
729 | } | ||
730 | else | ||
731 | { | ||
732 | // NOT IN () actually means everything so use a tautology | ||
733 | if ($negate) | ||
734 | { | ||
735 | return '1=1'; | ||
736 | } | ||
737 | // IN () actually means nothing so use a contradiction | ||
738 | else | ||
739 | { | ||
740 | return '1=0'; | ||
741 | } | ||
742 | } | ||
369 | } | 743 | } |
370 | if($query_id) | 744 | |
745 | if (!is_array($array)) | ||
371 | { | 746 | { |
372 | $result = @mysql_data_seek($query_id, $rownum); | 747 | $array = array($array); |
373 | return $result; | ||
374 | } | 748 | } |
749 | |||
750 | if (sizeof($array) == 1) | ||
751 | { | ||
752 | @reset($array); | ||
753 | $var = current($array); | ||
754 | |||
755 | return $field . ($negate ? ' <> ' : ' = ') . $this->sql_validate_value($var); | ||
756 | } | ||
375 | else | 757 | else |
376 | { | 758 | { |
377 | return false; | 759 | return $field . ($negate ? ' NOT IN ' : ' IN ') . '(' . implode(', ', array_map(array($this, 'sql_validate_value'), $array)) . ')'; |
378 | } | 760 | } |
379 | } | 761 | } |
380 | 762 | ||
381 | function sql_nextid(){ | 763 | /** |
382 | if($this->db_connect_id) | 764 | * Run more than one insert statement. |
765 | * | ||
766 | * @param string $table table name to run the statements on | ||
767 | * @param array &$sql_ary multi-dimensional array holding the statement data. | ||
768 | * | ||
769 | * @return bool false if no statements were executed. | ||
770 | * @access public | ||
771 | */ | ||
772 | function sql_multi_insert($table, &$sql_ary) | ||
773 | { | ||
774 | if (!sizeof($sql_ary)) | ||
383 | { | 775 | { |
384 | $result = @mysql_insert_id($this->db_connect_id); | 776 | return false; |
385 | return $result; | ||
386 | } | 777 | } |
778 | |||
779 | if ($this->multi_insert) | ||
780 | { | ||
781 | $ary = array(); | ||
782 | foreach ($sql_ary as $id => $_sql_ary) | ||
783 | { | ||
784 | // If by accident the sql array is only one-dimensional we build a normal insert statement | ||
785 | if (!is_array($_sql_ary)) | ||
786 | { | ||
787 | $this->sql_query('INSERT INTO ' . $table . ' ' . $this->sql_build_array('INSERT', $sql_ary)); | ||
788 | return true; | ||
789 | } | ||
790 | |||
791 | $values = array(); | ||
792 | foreach ($_sql_ary as $key => $var) | ||
793 | { | ||
794 | $values[] = $this->_sql_validate_value($var); | ||
795 | } | ||
796 | $ary[] = '(' . implode(', ', $values) . ')'; | ||
797 | } | ||
798 | |||
799 | $this->sql_query('INSERT INTO ' . $table . ' ' . ' (' . implode(', ', array_keys($sql_ary[0])) . ') VALUES ' . implode(', ', $ary)); | ||
800 | } | ||
387 | else | 801 | else |
388 | { | 802 | { |
389 | return false; | 803 | foreach ($sql_ary as $ary) |
804 | { | ||
805 | if (!is_array($ary)) | ||
806 | { | ||
807 | return false; | ||
808 | } | ||
809 | |||
810 | $this->sql_query('INSERT INTO ' . $table . ' ' . $this->sql_build_array('INSERT', $ary)); | ||
811 | } | ||
390 | } | 812 | } |
813 | |||
814 | return true; | ||
391 | } | 815 | } |
392 | 816 | ||
393 | function sql_freeresult($query_id = 0){ | 817 | /** |
394 | if(!$query_id) | 818 | * Free sql result |
819 | */ | ||
820 | function sql_freeresult($query_id = false) | ||
821 | { | ||
822 | global $cache; | ||
823 | |||
824 | if ($query_id === false) | ||
395 | { | 825 | { |
396 | $query_id = $this->query_result; | 826 | $query_id = $this->query_result; |
397 | } | 827 | } |
398 | 828 | ||
399 | if ( $query_id ) | 829 | if (isset($cache->sql_rowset[$query_id])) |
400 | { | 830 | { |
401 | unset($this->row[$query_id]); | 831 | return $cache->sql_freeresult($query_id); |
402 | unset($this->rowset[$query_id]); | 832 | } |
403 | 833 | ||
404 | @mysql_free_result($query_id); | 834 | if (isset($this->open_queries[(int) $query_id])) |
835 | { | ||
836 | unset($this->open_queries[(int) $query_id]); | ||
837 | return @mysql_free_result($query_id); | ||
838 | } | ||
405 | 839 | ||
406 | return true; | 840 | return false; |
841 | } | ||
842 | |||
843 | /** | ||
844 | * Errors handling | ||
845 | */ | ||
846 | function sql_error($sql = '') | ||
847 | { | ||
848 | global $lang; | ||
849 | |||
850 | // Set var to retrieve errored status | ||
851 | $this->sql_error_triggered = true; | ||
852 | $this->sql_error_sql = $sql; | ||
853 | |||
854 | $this->sql_error_returned = $this->_sql_error(); | ||
855 | |||
856 | if (!$this->return_on_error && !defined('IN_INSTALL')) | ||
857 | { | ||
858 | $message = '<b>SQL ERROR [ ' . SQL_LAYER . ' ]</b><br /><br />' . $this->sql_error_returned['message'] . ' [' . $this->sql_error_returned['code'] . ']'; | ||
859 | |||
860 | // Show complete SQL error and path to administrators only | ||
861 | // Additionally show complete error on installation or if extended debug mode is enabled | ||
862 | // The DEBUG_EXTRA constant is for development only! | ||
863 | if (defined('IN_INSTALL') || (defined('DEBUG_EXTRA') && (DEBUG_EXTRA == true))) | ||
864 | { | ||
865 | $backtrace = get_backtrace(); | ||
866 | |||
867 | $message .= ($sql) ? '<br /><br /><b>SQL</b><br /><br />' . htmlspecialchars($sql) : ''; | ||
868 | $message .= ($backtrace) ? '<br /><br /><b>BACKTRACE</b><br />' . $backtrace : ''; | ||
869 | $message .= '<br />'; | ||
870 | } | ||
871 | else | ||
872 | { | ||
873 | // If error occurs in initiating the session we need to use a pre-defined language string | ||
874 | // This could happen if the connection could not be established for example (then we are not able to grab the default language) | ||
875 | if (!isset($lang['SQL_ERROR_OCCURRED'])) | ||
876 | { | ||
877 | $message .= '<br /><br />An sql error occurred while fetching this page. Please contact site administrator if this problem persists.'; | ||
878 | } | ||
879 | else | ||
880 | { | ||
881 | $message .= '<br /><br />' . $lang['SQL_ERROR_OCCURRED']; | ||
882 | } | ||
883 | } | ||
884 | |||
885 | if ($this->transaction) | ||
886 | { | ||
887 | $this->sql_transaction('rollback'); | ||
888 | } | ||
889 | |||
890 | global $msg_code; | ||
891 | $msg_code = CRITICAL_MESSAGE; | ||
892 | $message = '<div style="text-align: left;">' . $message . '</div>'; | ||
893 | |||
894 | if (strlen($message) > 1024) | ||
895 | { | ||
896 | // We need to define $msg_long_text here to circumvent text stripping. | ||
897 | global $msg_long_text; | ||
898 | $msg_long_text = $message; | ||
899 | |||
900 | trigger_error(false, E_USER_NOTICE); | ||
901 | } | ||
902 | |||
903 | trigger_error($message, E_USER_NOTICE); | ||
904 | /* | ||
905 | $msg_text = $message; | ||
906 | $msg_title = isset($lang['Error']) ? $lang['Error'] : 'Error'; | ||
907 | message_die($msg_code, $msg_text, $msg_title, __LINE__, __FILE__, $sql); | ||
908 | */ | ||
407 | } | 909 | } |
408 | else | 910 | |
911 | if ($this->transaction) | ||
409 | { | 912 | { |
410 | return false; | 913 | $this->sql_transaction('rollback'); |
411 | } | 914 | } |
915 | |||
916 | return $this->sql_error_returned; | ||
412 | } | 917 | } |
413 | 918 | ||
414 | /** | 919 | /** |
... | ... | ||
416 | */ | 921 | */ |
417 | function sql_return_on_error($fail = false) | 922 | function sql_return_on_error($fail = false) |
418 | { | 923 | { |
419 | return true; | 924 | $this->sql_error_triggered = false; |
925 | $this->sql_error_sql = ''; | ||
926 | |||
927 | $this->return_on_error = $fail; | ||
420 | } | 928 | } |
421 | 929 | ||
422 | function sql_error($query_id = 0) | 930 | /** |
931 | * SQL Transaction | ||
932 | * @access private | ||
933 | */ | ||
934 | function sql_transaction($status = 'begin') | ||
423 | { | 935 | { |
424 | $result["message"] = @mysql_error($this->db_connect_id); | 936 | switch ($status) |
425 | $result["code"] = @mysql_errno($this->db_connect_id); | 937 | { |
938 | case 'begin': | ||
939 | // If we are within a transaction we will not open another one, but enclose the current one to not loose data (prevening auto commit) | ||
940 | if ($this->transaction) | ||
941 | { | ||
942 | $this->transactions++; | ||
943 | return true; | ||
944 | } | ||
426 | 945 | ||
946 | $result = $this->_sql_transaction('begin'); | ||
947 | |||
948 | if (!$result) | ||
949 | { | ||
950 | $this->sql_error(); | ||
951 | } | ||
952 | |||
953 | $this->transaction = true; | ||
954 | break; | ||
955 | |||
956 | case 'commit': | ||
957 | // If there was a previously opened transaction we do not commit yet... but count back the number of inner transactions | ||
958 | if ($this->transaction && $this->transactions) | ||
959 | { | ||
960 | $this->transactions--; | ||
961 | return true; | ||
962 | } | ||
963 | |||
964 | // Check if there is a transaction (no transaction can happen if there was an error, with a combined rollback and error returning enabled) | ||
965 | // This implies we have transaction always set for autocommit db's | ||
966 | if (!$this->transaction) | ||
967 | { | ||
968 | return false; | ||
969 | } | ||
970 | |||
971 | $result = $this->_sql_transaction('commit'); | ||
972 | |||
973 | if (!$result) | ||
974 | { | ||
975 | $this->sql_error(); | ||
976 | } | ||
977 | |||
978 | $this->transaction = false; | ||
979 | $this->transactions = 0; | ||
980 | break; | ||
981 | |||
982 | case 'rollback': | ||
983 | $result = $this->_sql_transaction('rollback'); | ||
984 | $this->transaction = false; | ||
985 | $this->transactions = 0; | ||
986 | break; | ||
987 | |||
988 | default: | ||
989 | $result = $this->_sql_transaction($status); | ||
990 | break; | ||
991 | } | ||
992 | |||
427 | return $result; | 993 | return $result; |
428 | } | 994 | } |
429 | 995 | ||
430 | } // class sql_db | 996 | /** |
997 | * Explain queries | ||
998 | */ | ||
999 | function sql_report($mode, $query = '') | ||
1000 | { | ||
1001 | if (empty($_REQUEST['explain'])) | ||
1002 | { | ||
1003 | return false; | ||
1004 | } | ||
431 | 1005 | ||
432 | } // if ... define | 1006 | if (!$query && ($this->query_hold != '')) |
1007 | { | ||
1008 | $query = $this->query_hold; | ||
1009 | } | ||
433 | 1010 | ||
1011 | switch ($mode) | ||
1012 | { | ||
1013 | case 'display': | ||
1014 | if (!empty($cache)) | ||
1015 | { | ||
1016 | $cache->unload(); | ||
1017 | } | ||
1018 | $this->sql_close(); | ||
1019 | |||
1020 | $mtime = explode(' ', microtime()); | ||
1021 | $totaltime = $mtime[0] + $mtime[1] - $this->sql_init_time; | ||
1022 | echo (' | ||
1023 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | ||
1024 | <html xmlns="http://www.w3.org/1999/xhtml"> | ||
1025 | <head> | ||
1026 | <link rel="stylesheet" href="' . IP_ROOT_PATH . 'templates/common/acp.css" type="text/css" /> | ||
1027 | <meta name="author" content="Mighty Gorgon" /> | ||
1028 | <title>Icy Phoenix</title> | ||
1029 | <!--[if lt IE 7]> | ||
1030 | <script type="text/javascript" src="' . IP_ROOT_PATH . 'templates/common/js/pngfix.js"></script> | ||
1031 | <![endif]--> | ||
1032 | </head> | ||
1033 | |||
1034 | <body> | ||
1035 | <a name="top"></a> | ||
1036 | <div id="global-wrapper" style="width: 960px; clear: both; margin: 0 auto;"> | ||
1037 | <div class="leftshadow"><div class="rightshadow"><div id="wrapper-inner"> | ||
1038 | <table id="forumtable" width="100%" cellspacing="0" cellpadding="0"> | ||
1039 | <tr> | ||
1040 | <td width="100%" colspan="3" valign="top"> | ||
1041 | <div id="top_logo"> | ||
1042 | <table class="" width="100%" cellspacing="0" cellpadding="0" border="0"> | ||
1043 | <tr> | ||
1044 | <td height="150" align="left" valign="middle"> | ||
1045 | <a href="http://www.icyphoenix.com" title="Icy Phoenix"><img src="' . IP_ROOT_PATH . 'images/logo_ip.png" alt="Icy Phoenix" title="Icy Phoenix" border="0" /></a> | ||
1046 | </td> | ||
1047 | </tr> | ||
1048 | </table> | ||
1049 | </div> | ||
1050 | </td> | ||
1051 | </tr> | ||
1052 | <tr><td colspan="3" class="forum-buttons" valign="middle">Icy Phoenix Extra Debug</td></tr> | ||
1053 | <tr> | ||
1054 | <td colspan="3" id="content"> | ||
1055 | <div class="post-text"> | ||
1056 | <br /> | ||
1057 | <h1>SQL Report</h1> | ||
1058 | <br /> | ||
1059 | <p><b>Page generated in ' . round($totaltime, 4) . " seconds with {$this->num_queries['normal']} queries" .
(($this->num_queries['cached']) ? " + {$this->num_queries['cached']} " . (($this->num_queries['cached'] == 1) ? 'query' : 'queries') . '
returning data from cache' : '') . '</b></p> | ||
1060 | <p>Time spent on ' . $this->num_queries['total'] . ' queries: <b>' . round($this->sql_time, 5) . 's</b></p> | ||
1061 | <p>Time spent on PHP: <b>' . round($totaltime - $this->sql_time, 5) . 's</b></p> | ||
1062 | <br /><br /> | ||
1063 | ' . $this->sql_report . ' | ||
1064 | </div> | ||
1065 | </td> | ||
1066 | </tr> | ||
1067 | <tr> | ||
1068 | <td width="100%" colspan="3"> | ||
1069 | <div id="bottom_logo_ext"> | ||
1070 | <div id="bottom_logo"> | ||
1071 | <table class="empty-table" width="100%" cellspacing="0" cellpadding="0" border="0"> | ||
1072 | <tr> | ||
1073 | <td nowrap="nowrap" width="45%" align="left"> | ||
1074 | <br /><span class="copyright"> Powered by <a href="http://www.icyphoenix.com/" target="_blank">Icy Phoenix</a> based on <a href="http://www.phpbb.com/" target="_blank">phpBB</a></span><br /><br /> | ||
1075 | </td> | ||
1076 | <td nowrap="nowrap" align="center"><div style="text-align:center;"> </div></td> | ||
1077 | <td nowrap="nowrap" width="45%" align="right"> | ||
1078 | <br /><span class="copyright">Design by <a href="http://www.mightygorgon.com" target="_blank">Mighty Gorgon</a> </span><br /><br /> | ||
1079 | </td> | ||
1080 | </tr> | ||
1081 | </table> | ||
1082 | </div> | ||
1083 | </div> | ||
1084 | </td> | ||
1085 | </tr> | ||
1086 | </table> | ||
1087 | </div></div></div> | ||
1088 | </div> | ||
1089 | </body> | ||
1090 | </html> | ||
1091 | '); | ||
1092 | exit_handler(); | ||
1093 | exit; | ||
1094 | break; | ||
1095 | |||
1096 | case 'stop': | ||
1097 | $endtime = explode(' ', microtime()); | ||
1098 | $endtime = $endtime[0] + $endtime[1]; | ||
1099 | $this->sql_report .= ' | ||
1100 | <table class="forumline" width="100%" cellspacing="0" cellpadding="0" border="0"> | ||
1101 | <thead> | ||
1102 | <tr><th>Query #' . $this->num_queries['total'] . '</th></tr> | ||
1103 | </thead> | ||
1104 | <tbody> | ||
1105 | <tr> | ||
1106 | <td class="row1"><textarea style="font-family: \'Courier New\', monospace; width: 99%;" rows="8"
cols="160">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n",
$query))) . '</textarea></td> | ||
1107 | </tr> | ||
1108 | </tbody> | ||
1109 | </table> | ||
1110 | ' . $this->html_hold . ' | ||
1111 | <p class="helpline" style="padding: 2px;"> | ||
1112 | '; | ||
1113 | if ($this->query_result) | ||
1114 | { | ||
1115 | $this->sql_report .= 'Elapsed: <b style="color:#224488;">' . sprintf('%.5f', $endtime - $this->curtime) . 's</b> •
[Before: ' . sprintf('%.5f', $this->curtime - $this->sql_start_time) . 's | After: ' . sprintf('%.5f', $endtime - $this->sql_start_time) . 's]'; | ||
1116 | if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query)) | ||
1117 | { | ||
1118 | $this->sql_report .= ' - [Affected rows: <b style="color:#224488;">' . $this->sql_affectedrows($this->query_result) . '</b>]'; | ||
1119 | } | ||
1120 | } | ||
1121 | else | ||
1122 | { | ||
1123 | $error = $this->sql_error(); | ||
1124 | $this->sql_report .= '<b style="color:#cc3333;">FAILED</b> - ' . SQL_LAYER . ' Error ' . $error['code'] . ': ' . htmlspecialchars($error['message']); | ||
1125 | } | ||
1126 | $this->sql_report .= '</p><br /><br />'; | ||
1127 | $this->sql_time += $endtime - $this->curtime; | ||
1128 | break; | ||
1129 | |||
1130 | case 'start': | ||
1131 | $this->query_hold = $query; | ||
1132 | $this->html_hold = ''; | ||
1133 | $this->curtime = explode(' ', microtime()); | ||
1134 | $this->curtime = $this->curtime[0] + $this->curtime[1]; | ||
1135 | break; | ||
1136 | |||
1137 | case 'add_select_row': | ||
1138 | |||
1139 | $html_table = func_get_arg(2); | ||
1140 | $row = func_get_arg(3); | ||
1141 | |||
1142 | if (!$html_table && sizeof($row)) | ||
1143 | { | ||
1144 | $html_table = true; | ||
1145 | $this->html_hold .= '<table class="forumline" width="100%" cellspacing="0" cellpadding="0" border="0"><tr>'; | ||
1146 | |||
1147 | foreach (array_keys($row) as $val) | ||
1148 | { | ||
1149 | $this->html_hold .= '<th>' . (($val) ? ucwords(str_replace('_', ' ', $val)) : ' ') . '</th>'; | ||
1150 | } | ||
1151 | $this->html_hold .= '</tr>'; | ||
1152 | } | ||
1153 | $this->html_hold .= '<tr>'; | ||
1154 | |||
1155 | $class = 'row1'; | ||
1156 | foreach (array_values($row) as $val) | ||
1157 | { | ||
1158 | $class = ($class == 'row1') ? 'row2' : 'row1'; | ||
1159 | $this->html_hold .= '<td class="' . $class . '">' . (($val) ? $val : ' ') . '</td>'; | ||
1160 | } | ||
1161 | $this->html_hold .= '</tr>'; | ||
1162 | |||
1163 | return $html_table; | ||
1164 | |||
1165 | break; | ||
1166 | |||
1167 | case 'fromcache': | ||
1168 | |||
1169 | $this->_sql_report($mode, $query); | ||
1170 | |||
1171 | break; | ||
1172 | |||
1173 | case 'record_fromcache': | ||
1174 | |||
1175 | $endtime = func_get_arg(2); | ||
1176 | $splittime = func_get_arg(3); | ||
1177 | |||
1178 | $time_cache = $endtime - $this->curtime; | ||
1179 | $time_db = $splittime - $endtime; | ||
1180 | $color = ($time_db > $time_cache) ? '#228844' : '#cc3333'; | ||
1181 | |||
1182 | $this->sql_report .= '<table class="forumline" width="100%" cellspacing="0" cellpadding="0" border="0">'; | ||
1183 | $this->sql_report .= '<thead><tr><th>Query #' . $this->num_queries['total'] . ' [From Cache]</th></tr></thead><tbody><tr>'; | ||
1184 | $this->sql_report .= '<td class="row3"><textarea style="font-family: \'Courier New\', monospace; width: 99%;"
rows="8" cols="160">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/',
"\n", $query))) . '</textarea></td></tr></tbody></table>'; | ||
1185 | $this->sql_report .= '<p class="helpline" style="padding: 2px;">'; | ||
1186 | $this->sql_report .= 'Before: ' . sprintf('%.5f', $this->curtime - $this->sql_start_time) . 's | After: ' . sprintf('%.5f', $endtime -
$this->sql_start_time) . 's | Elapsed [cache]: <b style="color: ' . $color . '">' . sprintf('%.5f', ($time_cache)) . 's</b> | Elapsed
[db]: <b>' . sprintf('%.5f', $time_db) . 's</b></p><br /><br />'; | ||
1187 | |||
1188 | // Pad the start time to not interfere with page timing | ||
1189 | $this->sql_start_time += $time_db; | ||
1190 | |||
1191 | break; | ||
1192 | |||
1193 | default: | ||
1194 | $this->_sql_report($mode, $query); | ||
1195 | break; | ||
1196 | } | ||
1197 | |||
1198 | return true; | ||
1199 | } | ||
1200 | |||
1201 | /** | ||
1202 | * Build LIMIT query | ||
1203 | */ | ||
1204 | function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) | ||
1205 | { | ||
1206 | $this->query_result = false; | ||
1207 | |||
1208 | // if $total is set to 0 we do not want to limit the number of rows | ||
1209 | if ($total == 0) | ||
1210 | { | ||
1211 | // Having a value of -1 was always a bug | ||
1212 | $total = '18446744073709551615'; | ||
1213 | } | ||
1214 | |||
1215 | $query .= "\n LIMIT " . ((!empty($offset)) ? $offset . ', ' . $total : $total); | ||
1216 | |||
1217 | return $this->sql_query($query, $cache_ttl); | ||
1218 | } | ||
1219 | |||
1220 | /** | ||
1221 | * Build LIKE expression | ||
1222 | * @access private | ||
1223 | */ | ||
1224 | function _sql_like_expression($expression) | ||
1225 | { | ||
1226 | return $expression; | ||
1227 | } | ||
1228 | |||
1229 | /** | ||
1230 | * Build db-specific query data | ||
1231 | * @access private | ||
1232 | */ | ||
1233 | function _sql_custom_build($stage, $data) | ||
1234 | { | ||
1235 | switch ($stage) | ||
1236 | { | ||
1237 | case 'FROM': | ||
1238 | $data = '(' . $data . ')'; | ||
1239 | break; | ||
1240 | } | ||
1241 | |||
1242 | return $data; | ||
1243 | } | ||
1244 | |||
1245 | /** | ||
1246 | * SQL Transaction | ||
1247 | * @access private | ||
1248 | */ | ||
1249 | function _sql_transaction($status = 'begin') | ||
1250 | { | ||
1251 | switch ($status) | ||
1252 | { | ||
1253 | case 'begin': | ||
1254 | return @mysql_query('BEGIN', $this->db_connect_id); | ||
1255 | break; | ||
1256 | |||
1257 | case 'commit': | ||
1258 | return @mysql_query('COMMIT', $this->db_connect_id); | ||
1259 | break; | ||
1260 | |||
1261 | case 'rollback': | ||
1262 | return @mysql_query('ROLLBACK', $this->db_connect_id); | ||
1263 | break; | ||
1264 | } | ||
1265 | |||
1266 | return true; | ||
1267 | } | ||
1268 | |||
1269 | /** | ||
1270 | * return sql error array | ||
1271 | * @access private | ||
1272 | */ | ||
1273 | function _sql_error() | ||
1274 | { | ||
1275 | if (!$this->db_connect_id) | ||
1276 | { | ||
1277 | return array( | ||
1278 | 'message' => @mysql_error(), | ||
1279 | 'code' => @mysql_errno() | ||
1280 | ); | ||
1281 | } | ||
1282 | |||
1283 | return array( | ||
1284 | 'message' => @mysql_error($this->db_connect_id), | ||
1285 | 'code' => @mysql_errno($this->db_connect_id) | ||
1286 | ); | ||
1287 | } | ||
1288 | |||
1289 | /** | ||
1290 | * Build db-specific report | ||
1291 | * @access private | ||
1292 | */ | ||
1293 | function _sql_report($mode, $query = '') | ||
1294 | { | ||
1295 | static $test_prof; | ||
1296 | |||
1297 | // current detection method, might just switch to see the existance of INFORMATION_SCHEMA.PROFILING | ||
1298 | if ($test_prof === null) | ||
1299 | { | ||
1300 | $test_prof = false; | ||
1301 | if (version_compare($this->sql_server_info(true), '5.0.37', '>=') && version_compare($this->sql_server_info(true), '5.1', '<')) | ||
1302 | { | ||
1303 | $test_prof = true; | ||
1304 | } | ||
1305 | } | ||
1306 | |||
1307 | switch ($mode) | ||
1308 | { | ||
1309 | case 'start': | ||
1310 | |||
1311 | $explain_query = $query; | ||
1312 | if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m)) | ||
1313 | { | ||
1314 | $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2]; | ||
1315 | } | ||
1316 | elseif (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m)) | ||
1317 | { | ||
1318 | $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2]; | ||
1319 | } | ||
1320 | |||
1321 | if (preg_match('/^SELECT/', $explain_query)) | ||
1322 | { | ||
1323 | $html_table = false; | ||
1324 | |||
1325 | // begin profiling | ||
1326 | if ($test_prof) | ||
1327 | { | ||
1328 | @mysql_query('SET profiling = 1;', $this->db_connect_id); | ||
1329 | } | ||
1330 | |||
1331 | if ($result = @mysql_query("EXPLAIN $explain_query", $this->db_connect_id)) | ||
1332 | { | ||
1333 | while ($row = @mysql_fetch_assoc($result)) | ||
1334 | { | ||
1335 | $html_table = $this->sql_report('add_select_row', $query, $html_table, $row); | ||
1336 | } | ||
1337 | } | ||
1338 | @mysql_free_result($result); | ||
1339 | |||
1340 | if ($html_table) | ||
1341 | { | ||
1342 | $this->html_hold .= '</table>'; | ||
1343 | } | ||
1344 | |||
1345 | if ($test_prof) | ||
1346 | { | ||
1347 | $html_table = false; | ||
1348 | |||
1349 | // get the last profile | ||
1350 | if ($result = @mysql_query('SHOW PROFILE ALL;', $this->db_connect_id)) | ||
1351 | { | ||
1352 | $this->html_hold .= '<br />'; | ||
1353 | while ($row = @mysql_fetch_assoc($result)) | ||
1354 | { | ||
1355 | // make <unknown> HTML safe | ||
1356 | if (!empty($row['Source_function'])) | ||
1357 | { | ||
1358 | $row['Source_function'] = str_replace(array('<', '>'), array('<', '>'), $row['Source_function']); | ||
1359 | } | ||
1360 | |||
1361 | // remove unsupported features | ||
1362 | foreach ($row as $key => $val) | ||
1363 | { | ||
1364 | if ($val === null) | ||
1365 | { | ||
1366 | unset($row[$key]); | ||
1367 | } | ||
1368 | } | ||
1369 | $html_table = $this->sql_report('add_select_row', $query, $html_table, $row); | ||
1370 | } | ||
1371 | } | ||
1372 | @mysql_free_result($result); | ||
1373 | |||
1374 | if ($html_table) | ||
1375 | { | ||
1376 | $this->html_hold .= '</table>'; | ||
1377 | } | ||
1378 | |||
1379 | @mysql_query('SET profiling = 0;', $this->db_connect_id); | ||
1380 | } | ||
1381 | } | ||
1382 | |||
1383 | break; | ||
1384 | |||
1385 | case 'fromcache': | ||
1386 | $endtime = explode(' ', microtime()); | ||
1387 | $endtime = $endtime[0] + $endtime[1]; | ||
1388 | |||
1389 | $result = @mysql_query($query, $this->db_connect_id); | ||
1390 | while ($void = @mysql_fetch_assoc($result)) | ||
1391 | { | ||
1392 | // Take the time spent on parsing rows into account | ||
1393 | } | ||
1394 | @mysql_free_result($result); | ||
1395 | |||
1396 | $splittime = explode(' ', microtime()); | ||
1397 | $splittime = $splittime[0] + $splittime[1]; | ||
1398 | |||
1399 | $this->sql_report('record_fromcache', $query, $endtime, $splittime); | ||
1400 | |||
1401 | break; | ||
1402 | } | ||
1403 | } | ||
1404 | |||
1405 | /** | ||
1406 | * Close sql connection | ||
1407 | * @access private | ||
1408 | */ | ||
1409 | function _sql_close() | ||
1410 | { | ||
1411 | return @mysql_close($this->db_connect_id); | ||
1412 | } | ||
1413 | |||
1414 | /** | ||
1415 | * Cache clear function | ||
1416 | */ | ||
1417 | function clear_cache($cache_prefix = '', $cache_folder = SQL_CACHE_FOLDER, $files_per_step = 0) | ||
1418 | { | ||
1419 | $cache_folder = (empty($cache_folder) ? SQL_CACHE_FOLDER : $cache_folder); | ||
1420 | |||
1421 | $cache_prefix = 'sql_' . $cache_prefix; | ||
1422 | $cache_folder = (!empty($cache_folder) && @is_dir($cache_folder)) ? $cache_folder : SQL_CACHE_FOLDER; | ||
1423 | $cache_folder = ((@is_dir($cache_folder)) ? $cache_folder : @phpbb_realpath($cache_folder)); | ||
1424 | |||
1425 | $res = opendir($cache_folder); | ||
1426 | if($res) | ||
1427 | { | ||
1428 | $files_counter = 0; | ||
1429 | while(($file = readdir($res)) !== false) | ||
1430 | { | ||
1431 | if(!@is_dir($file) && (substr($file, 0, strlen($cache_prefix)) === $cache_prefix) && (substr($file, -(strlen(PHP_EXT) + 1)) === '.' . PHP_EXT)) | ||
1432 | { | ||
1433 | @unlink($cache_folder . $file); | ||
1434 | $files_counter++; | ||
1435 | } | ||
1436 | if (($files_per_step > 0) && ($files_counter >= $files_per_step)) | ||
1437 | { | ||
1438 | closedir($res); | ||
1439 | return $files_per_step; | ||
1440 | } | ||
1441 | } | ||
1442 | } | ||
1443 | @closedir($res); | ||
1444 | } | ||
1445 | |||
1446 | } | ||
1447 | |||
434 | ?> | 1448 | ?> |
Download diff