6 * @copyright (c) 2005 phpBB Group
7 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
14 if (!defined('IN_PHPBB'))
19 include_once($phpbb_root_path . 'includes/db/dbal.' . $phpEx);
22 * MySQLi Database Abstraction Layer
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';
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('');
86 * Version information about used database
88 function sql_server_info()
90 return 'MySQL(i) ' . @mysqli_get_server_info
($this->db_connect_id
);
97 function _sql_transaction($status = 'begin')
102 return @mysqli_autocommit
($this->db_connect_id
, false);
106 $result = @mysqli_commit
($this->db_connect_id
);
107 @mysqli_autocommit
($this->db_connect_id
, true);
112 $result = @mysqli_rollback
($this->db_connect_id
);
113 @mysqli_autocommit
($this->db_connect_id
, true);
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
130 function sql_query($query = '', $cache_ttl = 0)
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);
172 return ($this->query_result
) ?
$this->query_result
: false;
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
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;
205 function sql_fetchrow($query_id = false)
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;
233 function sql_freeresult($query_id = false)
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)
265 case 'length_varchar':
267 return 'LENGTH(' . $col . ')';
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) . ')');
280 $query = "UPDATE $table SET ";
283 foreach (array_keys($data) as $key)
287 $query .= implode(', ', $set);
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
311 function _sql_like_expression($expression)
317 * Build db-specific query data
320 function _sql_custom_build($stage, $data)
325 $data = '(' . $data . ')';
333 * return sql error array
336 function _sql_error()
338 if (!$this->db_connect_id
)
341 'message' => @mysqli_connect_error
(),
342 'code' => @mysqli_connect_errno
()
347 'message' => @mysqli_error
($this->db_connect_id
),
348 'code' => @mysqli_errno
($this->db_connect_id
)
353 * Close sql connection
356 function _sql_close()
358 return @mysqli_close
($this->db_connect_id
);
362 * Build db-specific report
365 function _sql_report($mode, $query = '')
369 // current detection method, might just switch to see the existance of INFORMATION_SCHEMA.PROFILING
370 if ($test_prof === null)
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)
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))
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);
418 $this->html_hold
.= '</table>';
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('<', '>'), $row['Source_function']);
437 // remove unsupported features
438 foreach ($row as $key => $val)
445 $html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
448 @mysqli_free_result
($result);
452 $this->html_hold
.= '</table>';
455 @mysqli_query
($this->db_connect_id
, 'SET profiling = 0;');
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);