Some neat stuff, the new DBAL function has the potential of removing somewhere in...
[phpbb.git] / phpBB / includes / db / mysqli.php
blob48ea8d3c53a5b8683358283078d3e6fa165c7942
1 <?php
2 /**
4 * @package dbal
5 * @version $Id$
6 * @copyright (c) 2005 phpBB Group
7 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
9 */
11 /**
12 * @ignore
14 if (!defined('IN_PHPBB'))
16 exit;
19 include_once($phpbb_root_path . 'includes/db/dbal.' . $phpEx);
21 /**
22 * MySQLi Database Abstraction Layer
23 * Compatible with:
24 * MySQL 4.1+
25 * MySQL 5.0+
26 * @package dbal
28 class dbal_mysqli extends dbal
30 var $multi_insert = true;
32 // Supports multiple table deletion
33 var $multi_table_deletion = true;
35 var $dbms_type = 'mysql';
37 /**
38 * Connect to server
40 function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false , $new_link = false)
42 $this->persistency = $persistency;
43 $this->user = $sqluser;
44 $this->server = $sqlserver;
45 $this->dbname = $database;
46 $port = (!$port) ? NULL : $port;
48 // Persistant connections not supported by the mysqli extension?
49 $this->db_connect_id = @mysqli_connect($this->server, $this->user, $sqlpassword, $this->dbname, $port);
51 if ($this->db_connect_id && $this->dbname != '')
53 @mysqli_query($this->db_connect_id, "SET NAMES 'utf8'");
54 // enforce strict mode on databases that support it
55 if (mysqli_get_server_version($this->db_connect_id) >= 50002)
57 $result = @mysqli_query($this->db_connect_id, 'SELECT @@session.sql_mode AS sql_mode');
58 $row = @mysqli_fetch_assoc($result);
59 @mysqli_free_result($result);
60 $modes = array_map('trim', explode(',', $row['sql_mode']));
62 // TRADITIONAL includes STRICT_ALL_TABLES and STRICT_TRANS_TABLES
63 if (!in_array('TRADITIONAL', $modes))
65 if (!in_array('STRICT_ALL_TABLES', $modes))
67 $modes[] = 'STRICT_ALL_TABLES';
70 if (!in_array('STRICT_TRANS_TABLES', $modes))
72 $modes[] = 'STRICT_TRANS_TABLES';
76 $mode = implode(',', $modes);
77 @mysqli_query($this->db_connect_id, "SET SESSION sql_mode='{$mode}'");
79 return $this->db_connect_id;
82 return $this->sql_error('');
85 /**
86 * Version information about used database
88 function sql_server_info()
90 return 'MySQL(i) ' . @mysqli_get_server_info($this->db_connect_id);
93 /**
94 * SQL Transaction
95 * @access private
97 function _sql_transaction($status = 'begin')
99 switch ($status)
101 case 'begin':
102 return @mysqli_autocommit($this->db_connect_id, false);
103 break;
105 case 'commit':
106 $result = @mysqli_commit($this->db_connect_id);
107 @mysqli_autocommit($this->db_connect_id, true);
108 return $result;
109 break;
111 case 'rollback':
112 $result = @mysqli_rollback($this->db_connect_id);
113 @mysqli_autocommit($this->db_connect_id, true);
114 return $result;
115 break;
118 return true;
122 * Base query method
124 * @param string $query Contains the SQL query which shall be executed
125 * @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
126 * @return mixed When casted to bool the returned value returns true on success and false on failure
128 * @access public
130 function sql_query($query = '', $cache_ttl = 0)
132 if ($query != '')
134 global $cache;
136 // EXPLAIN only in extra debug mode
137 if (defined('DEBUG_EXTRA'))
139 $this->sql_report('start', $query);
142 $this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
143 $this->sql_add_num_queries($this->query_result);
145 if ($this->query_result === false)
147 if (($this->query_result = @mysqli_query($this->db_connect_id, $query)) === false)
149 $this->sql_error($query);
152 if (defined('DEBUG_EXTRA'))
154 $this->sql_report('stop', $query);
157 if ($cache_ttl && method_exists($cache, 'sql_save'))
159 $cache->sql_save($query, $this->query_result, $cache_ttl);
162 else if (defined('DEBUG_EXTRA'))
164 $this->sql_report('fromcache', $query);
167 else
169 return false;
172 return ($this->query_result) ? $this->query_result : false;
176 * Build LIMIT query
178 function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
180 $this->query_result = false;
182 // if $total is set to 0 we do not want to limit the number of rows
183 if ($total == 0)
185 // MySQL 4.1+ no longer supports -1 in limit queries
186 $total = '18446744073709551615';
189 $query .= "\n LIMIT " . ((!empty($offset)) ? $offset . ', ' . $total : $total);
191 return $this->sql_query($query, $cache_ttl);
195 * Return number of affected rows
197 function sql_affectedrows()
199 return ($this->db_connect_id) ? @mysqli_affected_rows($this->db_connect_id) : false;
203 * Fetch current row
205 function sql_fetchrow($query_id = false)
207 global $cache;
209 if ($query_id === false)
211 $query_id = $this->query_result;
214 if (!is_object($query_id) && isset($cache->sql_rowset[$query_id]))
216 return $cache->sql_fetchrow($query_id);
219 return ($query_id !== false) ? @mysqli_fetch_assoc($query_id) : false;
223 * Get last inserted id after insert statement
225 function sql_nextid()
227 return ($this->db_connect_id) ? @mysqli_insert_id($this->db_connect_id) : false;
231 * Free sql result
233 function sql_freeresult($query_id = false)
235 global $cache;
237 if ($query_id === false)
239 $query_id = $this->query_result;
242 if (!is_object($query_id) && isset($cache->sql_rowset[$query_id]))
244 return $cache->sql_freeresult($query_id);
247 return @mysqli_free_result($query_id);
251 * Escape string used in sql query
253 function sql_escape($msg)
255 return @mysqli_real_escape_string($this->db_connect_id, $msg);
259 * Expose a DBMS specific function
261 function sql_function($type, $col)
263 switch ($type)
265 case 'length_varchar':
266 case 'length_text':
267 return 'LENGTH(' . $col . ')';
268 break;
272 function sql_handle_data($type, $table, $data, $where = '')
274 if ($type === 'INSERT')
276 $stmt = mysqli_prepare($this->db_connect_id, "INSERT INTO $table (". implode(', ', array_keys($data)) . ") VALUES (" . substr(str_repeat('?, ', sizeof($data)) ,0, -1) . ')');
278 else
280 $query = "UPDATE $table SET ";
282 $set = array();
283 foreach (array_keys($data) as $key)
285 $set[] = "$key = ?";
287 $query .= implode(', ', $set);
289 if ($where !== '')
291 $query .= $where;
294 $stmt = mysqli_prepare($this->db_connect_id, $query);
297 // get the stmt onto the top of the function arguments
298 array_unshift($data, $stmt);
300 call_user_func_array('mysqli_stmt_bind_param', $data);
301 mysqli_stmt_execute($stmt);
303 mysqli_stmt_close($stmt);
308 * Build LIKE expression
309 * @access private
311 function _sql_like_expression($expression)
313 return $expression;
317 * Build db-specific query data
318 * @access private
320 function _sql_custom_build($stage, $data)
322 switch ($stage)
324 case 'FROM':
325 $data = '(' . $data . ')';
326 break;
329 return $data;
333 * return sql error array
334 * @access private
336 function _sql_error()
338 if (!$this->db_connect_id)
340 return array(
341 'message' => @mysqli_connect_error(),
342 'code' => @mysqli_connect_errno()
346 return array(
347 'message' => @mysqli_error($this->db_connect_id),
348 'code' => @mysqli_errno($this->db_connect_id)
353 * Close sql connection
354 * @access private
356 function _sql_close()
358 return @mysqli_close($this->db_connect_id);
362 * Build db-specific report
363 * @access private
365 function _sql_report($mode, $query = '')
367 static $test_prof;
369 // current detection method, might just switch to see the existance of INFORMATION_SCHEMA.PROFILING
370 if ($test_prof === null)
372 $test_prof = false;
373 if (strpos(mysqli_get_server_info($this->db_connect_id), 'community') !== false)
375 $ver = mysqli_get_server_version($this->db_connect_id);
376 if ($ver >= 50037 && $ver < 50100)
378 $test_prof = true;
383 switch ($mode)
385 case 'start':
387 $explain_query = $query;
388 if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
390 $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
392 else if (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
394 $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
397 if (preg_match('/^SELECT/', $explain_query))
399 $html_table = false;
401 // begin profiling
402 if ($test_prof)
404 @mysqli_query($this->db_connect_id, 'SET profiling = 1;');
407 if ($result = @mysqli_query($this->db_connect_id, "EXPLAIN $explain_query"))
409 while ($row = @mysqli_fetch_assoc($result))
411 $html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
414 @mysqli_free_result($result);
416 if ($html_table)
418 $this->html_hold .= '</table>';
421 if ($test_prof)
423 $html_table = false;
425 // get the last profile
426 if ($result = @mysqli_query($this->db_connect_id, 'SHOW PROFILE ALL;'))
428 $this->html_hold .= '<br />';
429 while ($row = @mysqli_fetch_assoc($result))
431 // make <unknown> HTML safe
432 if (!empty($row['Source_function']))
434 $row['Source_function'] = str_replace(array('<', '>'), array('&lt;', '&gt;'), $row['Source_function']);
437 // remove unsupported features
438 foreach ($row as $key => $val)
440 if ($val === null)
442 unset($row[$key]);
445 $html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
448 @mysqli_free_result($result);
450 if ($html_table)
452 $this->html_hold .= '</table>';
455 @mysqli_query($this->db_connect_id, 'SET profiling = 0;');
459 break;
461 case 'fromcache':
462 $endtime = explode(' ', microtime());
463 $endtime = $endtime[0] + $endtime[1];
465 $result = @mysqli_query($this->db_connect_id, $query);
466 while ($void = @mysqli_fetch_assoc($result))
468 // Take the time spent on parsing rows into account
470 @mysqli_free_result($result);
472 $splittime = explode(' ', microtime());
473 $splittime = $splittime[0] + $splittime[1];
475 $this->sql_report('record_fromcache', $query, $endtime, $splittime);
477 break;