mirror of
https://github.com/phpbb/phpbb.git
synced 2025-07-31 05:50:42 +02:00
[ticket/11015] Make DBAL classes autoloadable
PHPBB3-11015 This allows us to just create the object without having to include the driver first. However, it also means that users must specify the full class name in config.php
This commit is contained in:
1044
phpBB/includes/db/driver/driver.php
Normal file
1044
phpBB/includes/db/driver/driver.php
Normal file
File diff suppressed because it is too large
Load Diff
538
phpBB/includes/db/driver/firebird.php
Normal file
538
phpBB/includes/db/driver/firebird.php
Normal file
@@ -0,0 +1,538 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @package dbal
|
||||
* @copyright (c) 2005 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
if (!defined('IN_PHPBB'))
|
||||
{
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Firebird/Interbase Database Abstraction Layer
|
||||
* Minimum Requirement is Firebird 2.1
|
||||
* @package dbal
|
||||
*/
|
||||
class phpbb_db_driver_firebird extends phpbb_db_driver
|
||||
{
|
||||
var $last_query_text = '';
|
||||
var $service_handle = false;
|
||||
var $affected_rows = 0;
|
||||
var $connect_error = '';
|
||||
|
||||
/**
|
||||
* Connect to server
|
||||
*/
|
||||
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
|
||||
{
|
||||
$this->persistency = $persistency;
|
||||
$this->user = $sqluser;
|
||||
$this->server = $sqlserver . (($port) ? ':' . $port : '');
|
||||
$this->dbname = str_replace('\\', '/', $database);
|
||||
|
||||
// There are three possibilities to connect to an interbase db
|
||||
if (!$this->server)
|
||||
{
|
||||
$use_database = $this->dbname;
|
||||
}
|
||||
else if (strpos($this->server, '//') === 0)
|
||||
{
|
||||
$use_database = $this->server . $this->dbname;
|
||||
}
|
||||
else
|
||||
{
|
||||
$use_database = $this->server . ':' . $this->dbname;
|
||||
}
|
||||
|
||||
if ($this->persistency)
|
||||
{
|
||||
if (!function_exists('ibase_pconnect'))
|
||||
{
|
||||
$this->connect_error = 'ibase_pconnect function does not exist, is interbase extension installed?';
|
||||
return $this->sql_error('');
|
||||
}
|
||||
$this->db_connect_id = @ibase_pconnect($use_database, $this->user, $sqlpassword, false, false, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!function_exists('ibase_connect'))
|
||||
{
|
||||
$this->connect_error = 'ibase_connect function does not exist, is interbase extension installed?';
|
||||
return $this->sql_error('');
|
||||
}
|
||||
$this->db_connect_id = @ibase_connect($use_database, $this->user, $sqlpassword, false, false, 3);
|
||||
}
|
||||
|
||||
// Do not call ibase_service_attach if connection failed,
|
||||
// otherwise error message from ibase_(p)connect call will be clobbered.
|
||||
if ($this->db_connect_id && function_exists('ibase_service_attach') && $this->server)
|
||||
{
|
||||
$this->service_handle = @ibase_service_attach($this->server, $this->user, $sqlpassword);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->service_handle = false;
|
||||
}
|
||||
|
||||
return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Version information about used database
|
||||
* @param bool $raw if true, only return the fetched sql_server_version
|
||||
* @param bool $use_cache forced to false for Interbase
|
||||
* @return string sql server version
|
||||
*/
|
||||
function sql_server_info($raw = false, $use_cache = true)
|
||||
{
|
||||
/**
|
||||
* force $use_cache false. I didn't research why the caching code there is no caching code
|
||||
* but I assume its because the IB extension provides a direct method to access it
|
||||
* without a query.
|
||||
*/
|
||||
|
||||
$use_cache = false;
|
||||
|
||||
if ($this->service_handle !== false && function_exists('ibase_server_info'))
|
||||
{
|
||||
return @ibase_server_info($this->service_handle, IBASE_SVC_SERVER_VERSION);
|
||||
}
|
||||
|
||||
return ($raw) ? '2.1' : 'Firebird/Interbase';
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL Transaction
|
||||
* @access private
|
||||
*/
|
||||
function _sql_transaction($status = 'begin')
|
||||
{
|
||||
switch ($status)
|
||||
{
|
||||
case 'begin':
|
||||
return true;
|
||||
break;
|
||||
|
||||
case 'commit':
|
||||
return @ibase_commit();
|
||||
break;
|
||||
|
||||
case 'rollback':
|
||||
return @ibase_rollback();
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base query method
|
||||
*
|
||||
* @param string $query Contains the SQL query which shall be executed
|
||||
* @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
|
||||
* @return mixed When casted to bool the returned value returns true on success and false on failure
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function sql_query($query = '', $cache_ttl = 0)
|
||||
{
|
||||
if ($query != '')
|
||||
{
|
||||
global $cache;
|
||||
|
||||
// EXPLAIN only in extra debug mode
|
||||
if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('start', $query);
|
||||
}
|
||||
|
||||
$this->last_query_text = $query;
|
||||
$this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
|
||||
$this->sql_add_num_queries($this->query_result);
|
||||
|
||||
if ($this->query_result === false)
|
||||
{
|
||||
$array = array();
|
||||
// We overcome Firebird's 32767 char limit by binding vars
|
||||
if (strlen($query) > 32767)
|
||||
{
|
||||
if (preg_match('/^(INSERT INTO[^(]++)\\(([^()]+)\\) VALUES[^(]++\\((.*?)\\)$/s', $query, $regs))
|
||||
{
|
||||
if (strlen($regs[3]) > 32767)
|
||||
{
|
||||
preg_match_all('/\'(?:[^\']++|\'\')*+\'|[\d-.]+/', $regs[3], $vals, PREG_PATTERN_ORDER);
|
||||
|
||||
$inserts = $vals[0];
|
||||
unset($vals);
|
||||
|
||||
foreach ($inserts as $key => $value)
|
||||
{
|
||||
if (!empty($value) && $value[0] === "'" && strlen($value) > 32769) // check to see if this thing is greater than the max + 'x2
|
||||
{
|
||||
$inserts[$key] = '?';
|
||||
$array[] = str_replace("''", "'", substr($value, 1, -1));
|
||||
}
|
||||
}
|
||||
|
||||
$query = $regs[1] . '(' . $regs[2] . ') VALUES (' . implode(', ', $inserts) . ')';
|
||||
}
|
||||
}
|
||||
else if (preg_match('/^(UPDATE ([\\w_]++)\\s+SET )([\\w_]++\\s*=\\s*(?:\'(?:[^\']++|\'\')*+\'|\\d+)(?:,\\s*[\\w_]++\\s*=\\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]+))*+)\\s+(WHERE.*)$/s', $query, $data))
|
||||
{
|
||||
if (strlen($data[3]) > 32767)
|
||||
{
|
||||
$update = $data[1];
|
||||
$where = $data[4];
|
||||
preg_match_all('/(\\w++)\\s*=\\s*(\'(?:[^\']++|\'\')*+\'|[\d-.]++)/', $data[3], $temp, PREG_SET_ORDER);
|
||||
unset($data);
|
||||
|
||||
$cols = array();
|
||||
foreach ($temp as $value)
|
||||
{
|
||||
if (!empty($value[2]) && $value[2][0] === "'" && strlen($value[2]) > 32769) // check to see if this thing is greater than the max + 'x2
|
||||
{
|
||||
$array[] = str_replace("''", "'", substr($value[2], 1, -1));
|
||||
$cols[] = $value[1] . '=?';
|
||||
}
|
||||
else
|
||||
{
|
||||
$cols[] = $value[1] . '=' . $value[2];
|
||||
}
|
||||
}
|
||||
|
||||
$query = $update . implode(', ', $cols) . ' ' . $where;
|
||||
unset($cols);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('ibase_affected_rows') && (preg_match('/^UPDATE ([\w_]++)\s+SET [\w_]++\s*=\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]+)(?:,\s*[\w_]++\s*=\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]+))*+\s+(WHERE.*)?$/s', $query, $regs) || preg_match('/^DELETE FROM ([\w_]++)\s*(WHERE\s*.*)?$/s', $query, $regs)))
|
||||
{
|
||||
$affected_sql = 'SELECT COUNT(*) as num_rows_affected FROM ' . $regs[1];
|
||||
if (!empty($regs[2]))
|
||||
{
|
||||
$affected_sql .= ' ' . $regs[2];
|
||||
}
|
||||
|
||||
if (!($temp_q_id = @ibase_query($this->db_connect_id, $affected_sql)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$temp_result = @ibase_fetch_assoc($temp_q_id);
|
||||
@ibase_free_result($temp_q_id);
|
||||
|
||||
$this->affected_rows = ($temp_result) ? $temp_result['NUM_ROWS_AFFECTED'] : false;
|
||||
}
|
||||
|
||||
if (sizeof($array))
|
||||
{
|
||||
$p_query = @ibase_prepare($this->db_connect_id, $query);
|
||||
array_unshift($array, $p_query);
|
||||
$this->query_result = call_user_func_array('ibase_execute', $array);
|
||||
unset($array);
|
||||
|
||||
if ($this->query_result === false)
|
||||
{
|
||||
$this->sql_error($query);
|
||||
}
|
||||
}
|
||||
else if (($this->query_result = @ibase_query($this->db_connect_id, $query)) === false)
|
||||
{
|
||||
$this->sql_error($query);
|
||||
}
|
||||
|
||||
if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('stop', $query);
|
||||
}
|
||||
|
||||
if (!$this->transaction)
|
||||
{
|
||||
if (function_exists('ibase_commit_ret'))
|
||||
{
|
||||
@ibase_commit_ret();
|
||||
}
|
||||
else
|
||||
{
|
||||
// way cooler than ibase_commit_ret :D
|
||||
@ibase_query('COMMIT RETAIN;');
|
||||
}
|
||||
}
|
||||
|
||||
if ($cache_ttl && method_exists($cache, 'sql_save'))
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
$cache->sql_save($query, $this->query_result, $cache_ttl);
|
||||
}
|
||||
else if (strpos($query, 'SELECT') === 0 && $this->query_result)
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
}
|
||||
}
|
||||
else if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('fromcache', $query);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->query_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIMIT query
|
||||
*/
|
||||
function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
|
||||
{
|
||||
$this->query_result = false;
|
||||
|
||||
$query = 'SELECT FIRST ' . $total . ((!empty($offset)) ? ' SKIP ' . $offset : '') . substr($query, 6);
|
||||
|
||||
return $this->sql_query($query, $cache_ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return number of affected rows
|
||||
*/
|
||||
function sql_affectedrows()
|
||||
{
|
||||
// PHP 5+ function
|
||||
if (function_exists('ibase_affected_rows'))
|
||||
{
|
||||
return ($this->db_connect_id) ? @ibase_affected_rows($this->db_connect_id) : false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->affected_rows;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch current row
|
||||
*/
|
||||
function sql_fetchrow($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_fetchrow($query_id);
|
||||
}
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$row = array();
|
||||
$cur_row = @ibase_fetch_object($query_id, IBASE_TEXT);
|
||||
|
||||
if (!$cur_row)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (get_object_vars($cur_row) as $key => $value)
|
||||
{
|
||||
$row[strtolower($key)] = (is_string($value)) ? trim(str_replace(array("\\0", "\\n"), array("\0", "\n"), $value)) : $value;
|
||||
}
|
||||
|
||||
return (sizeof($row)) ? $row : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last inserted id after insert statement
|
||||
*/
|
||||
function sql_nextid()
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
|
||||
if ($query_id !== false && $this->last_query_text != '')
|
||||
{
|
||||
if ($this->query_result && preg_match('#^INSERT[\t\n ]+INTO[\t\n ]+([a-z0-9\_\-]+)#i', $this->last_query_text, $tablename))
|
||||
{
|
||||
$sql = 'SELECT GEN_ID(' . $tablename[1] . '_gen, 0) AS new_id FROM RDB$DATABASE';
|
||||
|
||||
if (!($temp_q_id = @ibase_query($this->db_connect_id, $sql)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$temp_result = @ibase_fetch_assoc($temp_q_id);
|
||||
@ibase_free_result($temp_q_id);
|
||||
|
||||
return ($temp_result) ? $temp_result['NEW_ID'] : false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free sql result
|
||||
*/
|
||||
function sql_freeresult($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_freeresult($query_id);
|
||||
}
|
||||
|
||||
if (isset($this->open_queries[(int) $query_id]))
|
||||
{
|
||||
unset($this->open_queries[(int) $query_id]);
|
||||
return @ibase_free_result($query_id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape string used in sql query
|
||||
*/
|
||||
function sql_escape($msg)
|
||||
{
|
||||
return str_replace(array("'", "\0"), array("''", ''), $msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIKE expression
|
||||
* @access private
|
||||
*/
|
||||
function _sql_like_expression($expression)
|
||||
{
|
||||
return $expression . " ESCAPE '\\'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific query data
|
||||
* @access private
|
||||
*/
|
||||
function _sql_custom_build($stage, $data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
function _sql_bit_and($column_name, $bit, $compare = '')
|
||||
{
|
||||
return 'BIN_AND(' . $column_name . ', ' . (1 << $bit) . ')' . (($compare) ? ' ' . $compare : '');
|
||||
}
|
||||
|
||||
function _sql_bit_or($column_name, $bit, $compare = '')
|
||||
{
|
||||
return 'BIN_OR(' . $column_name . ', ' . (1 << $bit) . ')' . (($compare) ? ' ' . $compare : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
function cast_expr_to_bigint($expression)
|
||||
{
|
||||
// Precision must be from 1 to 18
|
||||
return 'CAST(' . $expression . ' as DECIMAL(18, 0))';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
function cast_expr_to_string($expression)
|
||||
{
|
||||
return 'CAST(' . $expression . ' as VARCHAR(255))';
|
||||
}
|
||||
|
||||
/**
|
||||
* return sql error array
|
||||
* @access private
|
||||
*/
|
||||
function _sql_error()
|
||||
{
|
||||
// Need special handling here because ibase_errmsg returns
|
||||
// connection errors, however if the interbase extension
|
||||
// is not installed then ibase_errmsg does not exist and
|
||||
// we cannot call it.
|
||||
if (function_exists('ibase_errmsg'))
|
||||
{
|
||||
$msg = @ibase_errmsg();
|
||||
if (!$msg)
|
||||
{
|
||||
$msg = $this->connect_error;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$msg = $this->connect_error;
|
||||
}
|
||||
return array(
|
||||
'message' => $msg,
|
||||
'code' => (@function_exists('ibase_errcode') ? @ibase_errcode() : '')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close sql connection
|
||||
* @access private
|
||||
*/
|
||||
function _sql_close()
|
||||
{
|
||||
if ($this->service_handle !== false)
|
||||
{
|
||||
@ibase_service_detach($this->service_handle);
|
||||
}
|
||||
|
||||
return @ibase_close($this->db_connect_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific report
|
||||
* @access private
|
||||
*/
|
||||
function _sql_report($mode, $query = '')
|
||||
{
|
||||
switch ($mode)
|
||||
{
|
||||
case 'start':
|
||||
break;
|
||||
|
||||
case 'fromcache':
|
||||
$endtime = explode(' ', microtime());
|
||||
$endtime = $endtime[0] + $endtime[1];
|
||||
|
||||
$result = @ibase_query($this->db_connect_id, $query);
|
||||
while ($void = @ibase_fetch_object($result, IBASE_TEXT))
|
||||
{
|
||||
// Take the time spent on parsing rows into account
|
||||
}
|
||||
@ibase_free_result($result);
|
||||
|
||||
$splittime = explode(' ', microtime());
|
||||
$splittime = $splittime[0] + $splittime[1];
|
||||
|
||||
$this->sql_report('record_fromcache', $query, $endtime, $splittime);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
454
phpBB/includes/db/driver/mssql.php
Normal file
454
phpBB/includes/db/driver/mssql.php
Normal file
@@ -0,0 +1,454 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @package dbal
|
||||
* @copyright (c) 2005 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
if (!defined('IN_PHPBB'))
|
||||
{
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* MSSQL Database Abstraction Layer
|
||||
* Minimum Requirement is MSSQL 2000+
|
||||
* @package dbal
|
||||
*/
|
||||
class phpbb_db_driver_mssql extends phpbb_db_driver
|
||||
{
|
||||
/**
|
||||
* Connect to server
|
||||
*/
|
||||
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
|
||||
{
|
||||
$this->persistency = $persistency;
|
||||
$this->user = $sqluser;
|
||||
$this->dbname = $database;
|
||||
|
||||
$port_delimiter = (defined('PHP_OS') && substr(PHP_OS, 0, 3) === 'WIN') ? ',' : ':';
|
||||
$this->server = $sqlserver . (($port) ? $port_delimiter . $port : '');
|
||||
|
||||
@ini_set('mssql.charset', 'UTF-8');
|
||||
@ini_set('mssql.textlimit', 2147483647);
|
||||
@ini_set('mssql.textsize', 2147483647);
|
||||
|
||||
$this->db_connect_id = ($this->persistency) ? @mssql_pconnect($this->server, $this->user, $sqlpassword, $new_link) : @mssql_connect($this->server, $this->user, $sqlpassword, $new_link);
|
||||
|
||||
if ($this->db_connect_id && $this->dbname != '')
|
||||
{
|
||||
if (!@mssql_select_db($this->dbname, $this->db_connect_id))
|
||||
{
|
||||
@mssql_close($this->db_connect_id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Version information about used database
|
||||
* @param bool $raw if true, only return the fetched sql_server_version
|
||||
* @param bool $use_cache If true, it is safe to retrieve the value from the cache
|
||||
* @return string sql server version
|
||||
*/
|
||||
function sql_server_info($raw = false, $use_cache = true)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('mssql_version')) === false)
|
||||
{
|
||||
$result_id = @mssql_query("SELECT SERVERPROPERTY('productversion'), SERVERPROPERTY('productlevel'), SERVERPROPERTY('edition')", $this->db_connect_id);
|
||||
|
||||
$row = false;
|
||||
if ($result_id)
|
||||
{
|
||||
$row = @mssql_fetch_assoc($result_id);
|
||||
@mssql_free_result($result_id);
|
||||
}
|
||||
|
||||
$this->sql_server_version = ($row) ? trim(implode(' ', $row)) : 0;
|
||||
|
||||
if (!empty($cache) && $use_cache)
|
||||
{
|
||||
$cache->put('mssql_version', $this->sql_server_version);
|
||||
}
|
||||
}
|
||||
|
||||
if ($raw)
|
||||
{
|
||||
return $this->sql_server_version;
|
||||
}
|
||||
|
||||
return ($this->sql_server_version) ? 'MSSQL<br />' . $this->sql_server_version : 'MSSQL';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function sql_concatenate($expr1, $expr2)
|
||||
{
|
||||
return $expr1 . ' + ' . $expr2;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL Transaction
|
||||
* @access private
|
||||
*/
|
||||
function _sql_transaction($status = 'begin')
|
||||
{
|
||||
switch ($status)
|
||||
{
|
||||
case 'begin':
|
||||
return @mssql_query('BEGIN TRANSACTION', $this->db_connect_id);
|
||||
break;
|
||||
|
||||
case 'commit':
|
||||
return @mssql_query('COMMIT TRANSACTION', $this->db_connect_id);
|
||||
break;
|
||||
|
||||
case 'rollback':
|
||||
return @mssql_query('ROLLBACK TRANSACTION', $this->db_connect_id);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base query method
|
||||
*
|
||||
* @param string $query Contains the SQL query which shall be executed
|
||||
* @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
|
||||
* @return mixed When casted to bool the returned value returns true on success and false on failure
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function sql_query($query = '', $cache_ttl = 0)
|
||||
{
|
||||
if ($query != '')
|
||||
{
|
||||
global $cache;
|
||||
|
||||
// EXPLAIN only in extra debug mode
|
||||
if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('start', $query);
|
||||
}
|
||||
|
||||
$this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
|
||||
$this->sql_add_num_queries($this->query_result);
|
||||
|
||||
if ($this->query_result === false)
|
||||
{
|
||||
if (($this->query_result = @mssql_query($query, $this->db_connect_id)) === false)
|
||||
{
|
||||
$this->sql_error($query);
|
||||
}
|
||||
|
||||
if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('stop', $query);
|
||||
}
|
||||
|
||||
if ($cache_ttl && method_exists($cache, 'sql_save'))
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
$cache->sql_save($query, $this->query_result, $cache_ttl);
|
||||
}
|
||||
else if (strpos($query, 'SELECT') === 0 && $this->query_result)
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
}
|
||||
}
|
||||
else if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('fromcache', $query);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->query_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIMIT query
|
||||
*/
|
||||
function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
|
||||
{
|
||||
$this->query_result = false;
|
||||
|
||||
// Since TOP is only returning a set number of rows we won't need it if total is set to 0 (return all rows)
|
||||
if ($total)
|
||||
{
|
||||
// We need to grab the total number of rows + the offset number of rows to get the correct result
|
||||
if (strpos($query, 'SELECT DISTINCT') === 0)
|
||||
{
|
||||
$query = 'SELECT DISTINCT TOP ' . ($total + $offset) . ' ' . substr($query, 15);
|
||||
}
|
||||
else
|
||||
{
|
||||
$query = 'SELECT TOP ' . ($total + $offset) . ' ' . substr($query, 6);
|
||||
}
|
||||
}
|
||||
|
||||
$result = $this->sql_query($query, $cache_ttl);
|
||||
|
||||
// Seek by $offset rows
|
||||
if ($offset)
|
||||
{
|
||||
$this->sql_rowseek($offset, $result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return number of affected rows
|
||||
*/
|
||||
function sql_affectedrows()
|
||||
{
|
||||
return ($this->db_connect_id) ? @mssql_rows_affected($this->db_connect_id) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch current row
|
||||
*/
|
||||
function sql_fetchrow($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_fetchrow($query_id);
|
||||
}
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$row = @mssql_fetch_assoc($query_id);
|
||||
|
||||
// I hope i am able to remove this later... hopefully only a PHP or MSSQL bug
|
||||
if ($row)
|
||||
{
|
||||
foreach ($row as $key => $value)
|
||||
{
|
||||
$row[$key] = ($value === ' ' || $value === NULL) ? '' : $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek to given row number
|
||||
* rownum is zero-based
|
||||
*/
|
||||
function sql_rowseek($rownum, &$query_id)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_rowseek($rownum, $query_id);
|
||||
}
|
||||
|
||||
return ($query_id !== false) ? @mssql_data_seek($query_id, $rownum) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last inserted id after insert statement
|
||||
*/
|
||||
function sql_nextid()
|
||||
{
|
||||
$result_id = @mssql_query('SELECT SCOPE_IDENTITY()', $this->db_connect_id);
|
||||
if ($result_id)
|
||||
{
|
||||
if ($row = @mssql_fetch_assoc($result_id))
|
||||
{
|
||||
@mssql_free_result($result_id);
|
||||
return $row['computed'];
|
||||
}
|
||||
@mssql_free_result($result_id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free sql result
|
||||
*/
|
||||
function sql_freeresult($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_freeresult($query_id);
|
||||
}
|
||||
|
||||
if (isset($this->open_queries[$query_id]))
|
||||
{
|
||||
unset($this->open_queries[$query_id]);
|
||||
return @mssql_free_result($query_id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape string used in sql query
|
||||
*/
|
||||
function sql_escape($msg)
|
||||
{
|
||||
return str_replace(array("'", "\0"), array("''", ''), $msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_lower_text($column_name)
|
||||
{
|
||||
return "LOWER(SUBSTRING($column_name, 1, DATALENGTH($column_name)))";
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIKE expression
|
||||
* @access private
|
||||
*/
|
||||
function _sql_like_expression($expression)
|
||||
{
|
||||
return $expression . " ESCAPE '\\'";
|
||||
}
|
||||
|
||||
/**
|
||||
* return sql error array
|
||||
* @access private
|
||||
*/
|
||||
function _sql_error()
|
||||
{
|
||||
$error = array(
|
||||
'message' => @mssql_get_last_message(),
|
||||
'code' => ''
|
||||
);
|
||||
|
||||
// Get error code number
|
||||
$result_id = @mssql_query('SELECT @@ERROR as code', $this->db_connect_id);
|
||||
if ($result_id)
|
||||
{
|
||||
$row = @mssql_fetch_assoc($result_id);
|
||||
$error['code'] = $row['code'];
|
||||
@mssql_free_result($result_id);
|
||||
}
|
||||
|
||||
// Get full error message if possible
|
||||
$sql = 'SELECT CAST(description as varchar(255)) as message
|
||||
FROM master.dbo.sysmessages
|
||||
WHERE error = ' . $error['code'];
|
||||
$result_id = @mssql_query($sql);
|
||||
|
||||
if ($result_id)
|
||||
{
|
||||
$row = @mssql_fetch_assoc($result_id);
|
||||
if (!empty($row['message']))
|
||||
{
|
||||
$error['message'] .= '<br />' . $row['message'];
|
||||
}
|
||||
@mssql_free_result($result_id);
|
||||
}
|
||||
|
||||
return $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific query data
|
||||
* @access private
|
||||
*/
|
||||
function _sql_custom_build($stage, $data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close sql connection
|
||||
* @access private
|
||||
*/
|
||||
function _sql_close()
|
||||
{
|
||||
return @mssql_close($this->db_connect_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific report
|
||||
* @access private
|
||||
*/
|
||||
function _sql_report($mode, $query = '')
|
||||
{
|
||||
switch ($mode)
|
||||
{
|
||||
case 'start':
|
||||
$html_table = false;
|
||||
@mssql_query('SET SHOWPLAN_TEXT ON;', $this->db_connect_id);
|
||||
if ($result = @mssql_query($query, $this->db_connect_id))
|
||||
{
|
||||
@mssql_next_result($result);
|
||||
while ($row = @mssql_fetch_row($result))
|
||||
{
|
||||
$html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
|
||||
}
|
||||
}
|
||||
@mssql_query('SET SHOWPLAN_TEXT OFF;', $this->db_connect_id);
|
||||
@mssql_free_result($result);
|
||||
|
||||
if ($html_table)
|
||||
{
|
||||
$this->html_hold .= '</table>';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'fromcache':
|
||||
$endtime = explode(' ', microtime());
|
||||
$endtime = $endtime[0] + $endtime[1];
|
||||
|
||||
$result = @mssql_query($query, $this->db_connect_id);
|
||||
while ($void = @mssql_fetch_assoc($result))
|
||||
{
|
||||
// Take the time spent on parsing rows into account
|
||||
}
|
||||
@mssql_free_result($result);
|
||||
|
||||
$splittime = explode(' ', microtime());
|
||||
$splittime = $splittime[0] + $splittime[1];
|
||||
|
||||
$this->sql_report('record_fromcache', $query, $endtime, $splittime);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
395
phpBB/includes/db/driver/mssql_odbc.php
Normal file
395
phpBB/includes/db/driver/mssql_odbc.php
Normal file
@@ -0,0 +1,395 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @package dbal
|
||||
* @copyright (c) 2005 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
if (!defined('IN_PHPBB'))
|
||||
{
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unified ODBC functions
|
||||
* Unified ODBC functions support any database having ODBC driver, for example Adabas D, IBM DB2, iODBC, Solid, Sybase SQL Anywhere...
|
||||
* Here we only support MSSQL Server 2000+ because of the provided schema
|
||||
*
|
||||
* @note number of bytes returned for returning data depends on odbc.defaultlrl php.ini setting.
|
||||
* If it is limited to 4K for example only 4K of data is returned max, resulting in incomplete theme data for example.
|
||||
* @note odbc.defaultbinmode may affect UTF8 characters
|
||||
*
|
||||
* @package dbal
|
||||
*/
|
||||
class phpbb_db_driver_mssql_odbc extends phpbb_db_driver
|
||||
{
|
||||
var $last_query_text = '';
|
||||
|
||||
/**
|
||||
* Connect to server
|
||||
*/
|
||||
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
|
||||
{
|
||||
$this->persistency = $persistency;
|
||||
$this->user = $sqluser;
|
||||
$this->dbname = $database;
|
||||
|
||||
$port_delimiter = (defined('PHP_OS') && substr(PHP_OS, 0, 3) === 'WIN') ? ',' : ':';
|
||||
$this->server = $sqlserver . (($port) ? $port_delimiter . $port : '');
|
||||
|
||||
$max_size = @ini_get('odbc.defaultlrl');
|
||||
if (!empty($max_size))
|
||||
{
|
||||
$unit = strtolower(substr($max_size, -1, 1));
|
||||
$max_size = (int) $max_size;
|
||||
|
||||
if ($unit == 'k')
|
||||
{
|
||||
$max_size = floor($max_size / 1024);
|
||||
}
|
||||
else if ($unit == 'g')
|
||||
{
|
||||
$max_size *= 1024;
|
||||
}
|
||||
else if (is_numeric($unit))
|
||||
{
|
||||
$max_size = floor((int) ($max_size . $unit) / 1048576);
|
||||
}
|
||||
$max_size = max(8, $max_size) . 'M';
|
||||
|
||||
@ini_set('odbc.defaultlrl', $max_size);
|
||||
}
|
||||
|
||||
$this->db_connect_id = ($this->persistency) ? @odbc_pconnect($this->server, $this->user, $sqlpassword) : @odbc_connect($this->server, $this->user, $sqlpassword);
|
||||
|
||||
return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Version information about used database
|
||||
* @param bool $raw if true, only return the fetched sql_server_version
|
||||
* @param bool $use_cache If true, it is safe to retrieve the value from the cache
|
||||
* @return string sql server version
|
||||
*/
|
||||
function sql_server_info($raw = false, $use_cache = true)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('mssqlodbc_version')) === false)
|
||||
{
|
||||
$result_id = @odbc_exec($this->db_connect_id, "SELECT SERVERPROPERTY('productversion'), SERVERPROPERTY('productlevel'), SERVERPROPERTY('edition')");
|
||||
|
||||
$row = false;
|
||||
if ($result_id)
|
||||
{
|
||||
$row = @odbc_fetch_array($result_id);
|
||||
@odbc_free_result($result_id);
|
||||
}
|
||||
|
||||
$this->sql_server_version = ($row) ? trim(implode(' ', $row)) : 0;
|
||||
|
||||
if (!empty($cache) && $use_cache)
|
||||
{
|
||||
$cache->put('mssqlodbc_version', $this->sql_server_version);
|
||||
}
|
||||
}
|
||||
|
||||
if ($raw)
|
||||
{
|
||||
return $this->sql_server_version;
|
||||
}
|
||||
|
||||
return ($this->sql_server_version) ? 'MSSQL (ODBC)<br />' . $this->sql_server_version : 'MSSQL (ODBC)';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function sql_concatenate($expr1, $expr2)
|
||||
{
|
||||
return $expr1 . ' + ' . $expr2;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL Transaction
|
||||
* @access private
|
||||
*/
|
||||
function _sql_transaction($status = 'begin')
|
||||
{
|
||||
switch ($status)
|
||||
{
|
||||
case 'begin':
|
||||
return @odbc_exec($this->db_connect_id, 'BEGIN TRANSACTION');
|
||||
break;
|
||||
|
||||
case 'commit':
|
||||
return @odbc_exec($this->db_connect_id, 'COMMIT TRANSACTION');
|
||||
break;
|
||||
|
||||
case 'rollback':
|
||||
return @odbc_exec($this->db_connect_id, 'ROLLBACK TRANSACTION');
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base query method
|
||||
*
|
||||
* @param string $query Contains the SQL query which shall be executed
|
||||
* @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
|
||||
* @return mixed When casted to bool the returned value returns true on success and false on failure
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function sql_query($query = '', $cache_ttl = 0)
|
||||
{
|
||||
if ($query != '')
|
||||
{
|
||||
global $cache;
|
||||
|
||||
// EXPLAIN only in extra debug mode
|
||||
if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('start', $query);
|
||||
}
|
||||
|
||||
$this->last_query_text = $query;
|
||||
$this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
|
||||
$this->sql_add_num_queries($this->query_result);
|
||||
|
||||
if ($this->query_result === false)
|
||||
{
|
||||
if (($this->query_result = @odbc_exec($this->db_connect_id, $query)) === false)
|
||||
{
|
||||
$this->sql_error($query);
|
||||
}
|
||||
|
||||
if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('stop', $query);
|
||||
}
|
||||
|
||||
if ($cache_ttl && method_exists($cache, 'sql_save'))
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
$cache->sql_save($query, $this->query_result, $cache_ttl);
|
||||
}
|
||||
else if (strpos($query, 'SELECT') === 0 && $this->query_result)
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
}
|
||||
}
|
||||
else if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('fromcache', $query);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->query_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIMIT query
|
||||
*/
|
||||
function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
|
||||
{
|
||||
$this->query_result = false;
|
||||
|
||||
// Since TOP is only returning a set number of rows we won't need it if total is set to 0 (return all rows)
|
||||
if ($total)
|
||||
{
|
||||
// We need to grab the total number of rows + the offset number of rows to get the correct result
|
||||
if (strpos($query, 'SELECT DISTINCT') === 0)
|
||||
{
|
||||
$query = 'SELECT DISTINCT TOP ' . ($total + $offset) . ' ' . substr($query, 15);
|
||||
}
|
||||
else
|
||||
{
|
||||
$query = 'SELECT TOP ' . ($total + $offset) . ' ' . substr($query, 6);
|
||||
}
|
||||
}
|
||||
|
||||
$result = $this->sql_query($query, $cache_ttl);
|
||||
|
||||
// Seek by $offset rows
|
||||
if ($offset)
|
||||
{
|
||||
$this->sql_rowseek($offset, $result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return number of affected rows
|
||||
*/
|
||||
function sql_affectedrows()
|
||||
{
|
||||
return ($this->db_connect_id) ? @odbc_num_rows($this->query_result) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch current row
|
||||
* @note number of bytes returned depends on odbc.defaultlrl php.ini setting. If it is limited to 4K for example only 4K of data is returned max.
|
||||
*/
|
||||
function sql_fetchrow($query_id = false, $debug = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_fetchrow($query_id);
|
||||
}
|
||||
|
||||
return ($query_id !== false) ? @odbc_fetch_array($query_id) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last inserted id after insert statement
|
||||
*/
|
||||
function sql_nextid()
|
||||
{
|
||||
$result_id = @odbc_exec($this->db_connect_id, 'SELECT @@IDENTITY');
|
||||
|
||||
if ($result_id)
|
||||
{
|
||||
if (@odbc_fetch_array($result_id))
|
||||
{
|
||||
$id = @odbc_result($result_id, 1);
|
||||
@odbc_free_result($result_id);
|
||||
return $id;
|
||||
}
|
||||
@odbc_free_result($result_id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free sql result
|
||||
*/
|
||||
function sql_freeresult($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_freeresult($query_id);
|
||||
}
|
||||
|
||||
if (isset($this->open_queries[(int) $query_id]))
|
||||
{
|
||||
unset($this->open_queries[(int) $query_id]);
|
||||
return @odbc_free_result($query_id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape string used in sql query
|
||||
*/
|
||||
function sql_escape($msg)
|
||||
{
|
||||
return str_replace(array("'", "\0"), array("''", ''), $msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_lower_text($column_name)
|
||||
{
|
||||
return "LOWER(SUBSTRING($column_name, 1, DATALENGTH($column_name)))";
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIKE expression
|
||||
* @access private
|
||||
*/
|
||||
function _sql_like_expression($expression)
|
||||
{
|
||||
return $expression . " ESCAPE '\\'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific query data
|
||||
* @access private
|
||||
*/
|
||||
function _sql_custom_build($stage, $data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* return sql error array
|
||||
* @access private
|
||||
*/
|
||||
function _sql_error()
|
||||
{
|
||||
return array(
|
||||
'message' => @odbc_errormsg(),
|
||||
'code' => @odbc_error()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close sql connection
|
||||
* @access private
|
||||
*/
|
||||
function _sql_close()
|
||||
{
|
||||
return @odbc_close($this->db_connect_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific report
|
||||
* @access private
|
||||
*/
|
||||
function _sql_report($mode, $query = '')
|
||||
{
|
||||
switch ($mode)
|
||||
{
|
||||
case 'start':
|
||||
break;
|
||||
|
||||
case 'fromcache':
|
||||
$endtime = explode(' ', microtime());
|
||||
$endtime = $endtime[0] + $endtime[1];
|
||||
|
||||
$result = @odbc_exec($this->db_connect_id, $query);
|
||||
while ($void = @odbc_fetch_array($result))
|
||||
{
|
||||
// Take the time spent on parsing rows into account
|
||||
}
|
||||
@odbc_free_result($result);
|
||||
|
||||
$splittime = explode(' ', microtime());
|
||||
$splittime = $splittime[0] + $splittime[1];
|
||||
|
||||
$this->sql_report('record_fromcache', $query, $endtime, $splittime);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
642
phpBB/includes/db/driver/mssqlnative.php
Normal file
642
phpBB/includes/db/driver/mssqlnative.php
Normal file
@@ -0,0 +1,642 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @package dbal
|
||||
* @copyright (c) 2010 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
|
||||
*
|
||||
* This is the MS SQL Server Native database abstraction layer.
|
||||
* PHP mssql native driver required.
|
||||
* @author Chris Pucci
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
if (!defined('IN_PHPBB'))
|
||||
{
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prior to version 1.1 the SQL Server Native PHP driver didn't support sqlsrv_num_rows, or cursor based seeking so we recall all rows into an array
|
||||
* and maintain our own cursor index into that array.
|
||||
*/
|
||||
class result_mssqlnative
|
||||
{
|
||||
public function result_mssqlnative($queryresult = false)
|
||||
{
|
||||
$this->m_cursor = 0;
|
||||
$this->m_rows = array();
|
||||
$this->m_num_fields = sqlsrv_num_fields($queryresult);
|
||||
$this->m_field_meta = sqlsrv_field_metadata($queryresult);
|
||||
|
||||
while ($row = sqlsrv_fetch_array($queryresult, SQLSRV_FETCH_ASSOC))
|
||||
{
|
||||
if ($row !== null)
|
||||
{
|
||||
foreach($row as $k => $v)
|
||||
{
|
||||
if (is_object($v) && method_exists($v, 'format'))
|
||||
{
|
||||
$row[$k] = $v->format("Y-m-d\TH:i:s\Z");
|
||||
}
|
||||
}
|
||||
$this->m_rows[] = $row;//read results into memory, cursors are not supported
|
||||
}
|
||||
}
|
||||
|
||||
$this->m_row_count = sizeof($this->m_rows);
|
||||
}
|
||||
|
||||
private function array_to_obj($array, &$obj)
|
||||
{
|
||||
foreach ($array as $key => $value)
|
||||
{
|
||||
if (is_array($value))
|
||||
{
|
||||
$obj->$key = new stdClass();
|
||||
array_to_obj($value, $obj->$key);
|
||||
}
|
||||
else
|
||||
{
|
||||
$obj->$key = $value;
|
||||
}
|
||||
}
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function fetch($mode = SQLSRV_FETCH_BOTH, $object_class = 'stdClass')
|
||||
{
|
||||
if ($this->m_cursor >= $this->m_row_count || $this->m_row_count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$ret = false;
|
||||
$arr_num = array();
|
||||
|
||||
if ($mode == SQLSRV_FETCH_NUMERIC || $mode == SQLSRV_FETCH_BOTH)
|
||||
{
|
||||
foreach($this->m_rows[$this->m_cursor] as $key => $value)
|
||||
{
|
||||
$arr_num[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
switch ($mode)
|
||||
{
|
||||
case SQLSRV_FETCH_ASSOC:
|
||||
$ret = $this->m_rows[$this->m_cursor];
|
||||
break;
|
||||
case SQLSRV_FETCH_NUMERIC:
|
||||
$ret = $arr_num;
|
||||
break;
|
||||
case 'OBJECT':
|
||||
$ret = $this->array_to_obj($this->m_rows[$this->m_cursor], $o = new $object_class);
|
||||
break;
|
||||
case SQLSRV_FETCH_BOTH:
|
||||
default:
|
||||
$ret = $this->m_rows[$this->m_cursor] + $arr_num;
|
||||
break;
|
||||
}
|
||||
$this->m_cursor++;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function get($pos, $fld)
|
||||
{
|
||||
return $this->m_rows[$pos][$fld];
|
||||
}
|
||||
|
||||
public function num_rows()
|
||||
{
|
||||
return $this->m_row_count;
|
||||
}
|
||||
|
||||
public function seek($iRow)
|
||||
{
|
||||
$this->m_cursor = min($iRow, $this->m_row_count);
|
||||
}
|
||||
|
||||
public function num_fields()
|
||||
{
|
||||
return $this->m_num_fields;
|
||||
}
|
||||
|
||||
public function field_name($nr)
|
||||
{
|
||||
$arr_keys = array_keys($this->m_rows[0]);
|
||||
return $arr_keys[$nr];
|
||||
}
|
||||
|
||||
public function field_type($nr)
|
||||
{
|
||||
$i = 0;
|
||||
$int_type = -1;
|
||||
$str_type = '';
|
||||
|
||||
foreach ($this->m_field_meta as $meta)
|
||||
{
|
||||
if ($nr == $i)
|
||||
{
|
||||
$int_type = $meta['Type'];
|
||||
break;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
//http://msdn.microsoft.com/en-us/library/cc296183.aspx contains type table
|
||||
switch ($int_type)
|
||||
{
|
||||
case SQLSRV_SQLTYPE_BIGINT: $str_type = 'bigint'; break;
|
||||
case SQLSRV_SQLTYPE_BINARY: $str_type = 'binary'; break;
|
||||
case SQLSRV_SQLTYPE_BIT: $str_type = 'bit'; break;
|
||||
case SQLSRV_SQLTYPE_CHAR: $str_type = 'char'; break;
|
||||
case SQLSRV_SQLTYPE_DATETIME: $str_type = 'datetime'; break;
|
||||
case SQLSRV_SQLTYPE_DECIMAL/*($precision, $scale)*/: $str_type = 'decimal'; break;
|
||||
case SQLSRV_SQLTYPE_FLOAT: $str_type = 'float'; break;
|
||||
case SQLSRV_SQLTYPE_IMAGE: $str_type = 'image'; break;
|
||||
case SQLSRV_SQLTYPE_INT: $str_type = 'int'; break;
|
||||
case SQLSRV_SQLTYPE_MONEY: $str_type = 'money'; break;
|
||||
case SQLSRV_SQLTYPE_NCHAR/*($charCount)*/: $str_type = 'nchar'; break;
|
||||
case SQLSRV_SQLTYPE_NUMERIC/*($precision, $scale)*/: $str_type = 'numeric'; break;
|
||||
case SQLSRV_SQLTYPE_NVARCHAR/*($charCount)*/: $str_type = 'nvarchar'; break;
|
||||
case SQLSRV_SQLTYPE_NTEXT: $str_type = 'ntext'; break;
|
||||
case SQLSRV_SQLTYPE_REAL: $str_type = 'real'; break;
|
||||
case SQLSRV_SQLTYPE_SMALLDATETIME: $str_type = 'smalldatetime'; break;
|
||||
case SQLSRV_SQLTYPE_SMALLINT: $str_type = 'smallint'; break;
|
||||
case SQLSRV_SQLTYPE_SMALLMONEY: $str_type = 'smallmoney'; break;
|
||||
case SQLSRV_SQLTYPE_TEXT: $str_type = 'text'; break;
|
||||
case SQLSRV_SQLTYPE_TIMESTAMP: $str_type = 'timestamp'; break;
|
||||
case SQLSRV_SQLTYPE_TINYINT: $str_type = 'tinyint'; break;
|
||||
case SQLSRV_SQLTYPE_UNIQUEIDENTIFIER: $str_type = 'uniqueidentifier'; break;
|
||||
case SQLSRV_SQLTYPE_UDT: $str_type = 'UDT'; break;
|
||||
case SQLSRV_SQLTYPE_VARBINARY/*($byteCount)*/: $str_type = 'varbinary'; break;
|
||||
case SQLSRV_SQLTYPE_VARCHAR/*($charCount)*/: $str_type = 'varchar'; break;
|
||||
case SQLSRV_SQLTYPE_XML: $str_type = 'xml'; break;
|
||||
default: $str_type = $int_type;
|
||||
}
|
||||
return $str_type;
|
||||
}
|
||||
|
||||
public function free()
|
||||
{
|
||||
unset($this->m_rows);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @package dbal
|
||||
*/
|
||||
class phpbb_db_driver_mssqlnative extends phpbb_db_driver
|
||||
{
|
||||
var $m_insert_id = NULL;
|
||||
var $last_query_text = '';
|
||||
var $query_options = array();
|
||||
|
||||
/**
|
||||
* Connect to server
|
||||
*/
|
||||
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
|
||||
{
|
||||
# Test for driver support, to avoid suppressed fatal error
|
||||
if (!function_exists('sqlsrv_connect'))
|
||||
{
|
||||
trigger_error('Native MS SQL Server driver for PHP is missing or needs to be updated. Version 1.1 or later is required to install phpBB3. You can download the driver from: http://www.microsoft.com/sqlserver/2005/en/us/PHP-Driver.aspx\n', E_USER_ERROR);
|
||||
}
|
||||
|
||||
//set up connection variables
|
||||
$this->persistency = $persistency;
|
||||
$this->user = $sqluser;
|
||||
$this->dbname = $database;
|
||||
$port_delimiter = (defined('PHP_OS') && substr(PHP_OS, 0, 3) === 'WIN') ? ',' : ':';
|
||||
$this->server = $sqlserver . (($port) ? $port_delimiter . $port : '');
|
||||
|
||||
//connect to database
|
||||
error_reporting(E_ALL);
|
||||
$this->db_connect_id = sqlsrv_connect($this->server, array(
|
||||
'Database' => $this->dbname,
|
||||
'UID' => $this->user,
|
||||
'PWD' => $sqlpassword
|
||||
));
|
||||
|
||||
return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Version information about used database
|
||||
* @param bool $raw if true, only return the fetched sql_server_version
|
||||
* @param bool $use_cache If true, it is safe to retrieve the value from the cache
|
||||
* @return string sql server version
|
||||
*/
|
||||
function sql_server_info($raw = false, $use_cache = true)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('mssql_version')) === false)
|
||||
{
|
||||
$arr_server_info = sqlsrv_server_info($this->db_connect_id);
|
||||
$this->sql_server_version = $arr_server_info['SQLServerVersion'];
|
||||
|
||||
if (!empty($cache) && $use_cache)
|
||||
{
|
||||
$cache->put('mssql_version', $this->sql_server_version);
|
||||
}
|
||||
}
|
||||
|
||||
if ($raw)
|
||||
{
|
||||
return $this->sql_server_version;
|
||||
}
|
||||
|
||||
return ($this->sql_server_version) ? 'MSSQL<br />' . $this->sql_server_version : 'MSSQL';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function sql_concatenate($expr1, $expr2)
|
||||
{
|
||||
return $expr1 . ' + ' . $expr2;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_buffer_nested_transactions()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL Transaction
|
||||
* @access private
|
||||
*/
|
||||
function _sql_transaction($status = 'begin')
|
||||
{
|
||||
switch ($status)
|
||||
{
|
||||
case 'begin':
|
||||
return sqlsrv_begin_transaction($this->db_connect_id);
|
||||
break;
|
||||
|
||||
case 'commit':
|
||||
return sqlsrv_commit($this->db_connect_id);
|
||||
break;
|
||||
|
||||
case 'rollback':
|
||||
return sqlsrv_rollback($this->db_connect_id);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base query method
|
||||
*
|
||||
* @param string $query Contains the SQL query which shall be executed
|
||||
* @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
|
||||
* @return mixed When casted to bool the returned value returns true on success and false on failure
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function sql_query($query = '', $cache_ttl = 0)
|
||||
{
|
||||
if ($query != '')
|
||||
{
|
||||
global $cache;
|
||||
|
||||
// EXPLAIN only in extra debug mode
|
||||
if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('start', $query);
|
||||
}
|
||||
|
||||
$this->last_query_text = $query;
|
||||
$this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
|
||||
$this->sql_add_num_queries($this->query_result);
|
||||
|
||||
if ($this->query_result === false)
|
||||
{
|
||||
if (($this->query_result = @sqlsrv_query($this->db_connect_id, $query, array(), $this->query_options)) === false)
|
||||
{
|
||||
$this->sql_error($query);
|
||||
}
|
||||
// reset options for next query
|
||||
$this->query_options = array();
|
||||
|
||||
if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('stop', $query);
|
||||
}
|
||||
|
||||
if ($cache_ttl && method_exists($cache, 'sql_save'))
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
$cache->sql_save($query, $this->query_result, $cache_ttl);
|
||||
}
|
||||
else if (strpos($query, 'SELECT') === 0 && $this->query_result)
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
}
|
||||
}
|
||||
else if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('fromcache', $query);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return $this->query_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIMIT query
|
||||
*/
|
||||
function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
|
||||
{
|
||||
$this->query_result = false;
|
||||
|
||||
// total == 0 means all results - not zero results
|
||||
if ($offset == 0 && $total !== 0)
|
||||
{
|
||||
if (strpos($query, "SELECT") === false)
|
||||
{
|
||||
$query = "TOP {$total} " . $query;
|
||||
}
|
||||
else
|
||||
{
|
||||
$query = preg_replace('/SELECT(\s*DISTINCT)?/Dsi', 'SELECT$1 TOP '.$total, $query);
|
||||
}
|
||||
}
|
||||
else if ($offset > 0)
|
||||
{
|
||||
$query = preg_replace('/SELECT(\s*DISTINCT)?/Dsi', 'SELECT$1 TOP(10000000) ', $query);
|
||||
$query = 'SELECT *
|
||||
FROM (SELECT sub2.*, ROW_NUMBER() OVER(ORDER BY sub2.line2) AS line3
|
||||
FROM (SELECT 1 AS line2, sub1.* FROM (' . $query . ') AS sub1) as sub2) AS sub3';
|
||||
|
||||
if ($total > 0)
|
||||
{
|
||||
$query .= ' WHERE line3 BETWEEN ' . ($offset+1) . ' AND ' . ($offset + $total);
|
||||
}
|
||||
else
|
||||
{
|
||||
$query .= ' WHERE line3 > ' . $offset;
|
||||
}
|
||||
}
|
||||
|
||||
$result = $this->sql_query($query, $cache_ttl);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return number of affected rows
|
||||
*/
|
||||
function sql_affectedrows()
|
||||
{
|
||||
return (!empty($this->query_result)) ? @sqlsrv_rows_affected($this->query_result) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch current row
|
||||
*/
|
||||
function sql_fetchrow($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_fetchrow($query_id);
|
||||
}
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$row = @sqlsrv_fetch_array($query_id, SQLSRV_FETCH_ASSOC);
|
||||
|
||||
if ($row)
|
||||
{
|
||||
foreach ($row as $key => $value)
|
||||
{
|
||||
$row[$key] = ($value === ' ' || $value === NULL) ? '' : $value;
|
||||
}
|
||||
|
||||
// remove helper values from LIMIT queries
|
||||
if (isset($row['line2']))
|
||||
{
|
||||
unset($row['line2'], $row['line3']);
|
||||
}
|
||||
}
|
||||
return (sizeof($row)) ? $row : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last inserted id after insert statement
|
||||
*/
|
||||
function sql_nextid()
|
||||
{
|
||||
$result_id = @sqlsrv_query($this->db_connect_id, 'SELECT @@IDENTITY');
|
||||
|
||||
if ($result_id !== false)
|
||||
{
|
||||
$row = @sqlsrv_fetch_array($result_id);
|
||||
$id = $row[0];
|
||||
@sqlsrv_free_stmt($result_id);
|
||||
return $id;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Free sql result
|
||||
*/
|
||||
function sql_freeresult($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_freeresult($query_id);
|
||||
}
|
||||
|
||||
if (isset($this->open_queries[$query_id]))
|
||||
{
|
||||
unset($this->open_queries[$query_id]);
|
||||
return @sqlsrv_free_stmt($query_id);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape string used in sql query
|
||||
*/
|
||||
function sql_escape($msg)
|
||||
{
|
||||
return str_replace(array("'", "\0"), array("''", ''), $msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
function sql_lower_text($column_name)
|
||||
{
|
||||
return "LOWER(SUBSTRING($column_name, 1, DATALENGTH($column_name)))";
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIKE expression
|
||||
* @access private
|
||||
*/
|
||||
function _sql_like_expression($expression)
|
||||
{
|
||||
return $expression . " ESCAPE '\\'";
|
||||
}
|
||||
|
||||
/**
|
||||
* return sql error array
|
||||
* @access private
|
||||
*/
|
||||
function _sql_error()
|
||||
{
|
||||
$errors = @sqlsrv_errors(SQLSRV_ERR_ERRORS);
|
||||
$error_message = '';
|
||||
$code = 0;
|
||||
|
||||
if ($errors != null)
|
||||
{
|
||||
foreach ($errors as $error)
|
||||
{
|
||||
$error_message .= "SQLSTATE: ".$error[ 'SQLSTATE']."\n";
|
||||
$error_message .= "code: ".$error[ 'code']."\n";
|
||||
$code = $error['code'];
|
||||
$error_message .= "message: ".$error[ 'message']."\n";
|
||||
}
|
||||
$this->last_error_result = $error_message;
|
||||
$error = $this->last_error_result;
|
||||
}
|
||||
else
|
||||
{
|
||||
$error = (isset($this->last_error_result) && $this->last_error_result) ? $this->last_error_result : array();
|
||||
}
|
||||
|
||||
return array(
|
||||
'message' => $error,
|
||||
'code' => $code,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific query data
|
||||
* @access private
|
||||
*/
|
||||
function _sql_custom_build($stage, $data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close sql connection
|
||||
* @access private
|
||||
*/
|
||||
function _sql_close()
|
||||
{
|
||||
return @sqlsrv_close($this->db_connect_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific report
|
||||
* @access private
|
||||
*/
|
||||
function _sql_report($mode, $query = '')
|
||||
{
|
||||
switch ($mode)
|
||||
{
|
||||
case 'start':
|
||||
$html_table = false;
|
||||
@sqlsrv_query($this->db_connect_id, 'SET SHOWPLAN_TEXT ON;');
|
||||
if ($result = @sqlsrv_query($this->db_connect_id, $query))
|
||||
{
|
||||
@sqlsrv_next_result($result);
|
||||
while ($row = @sqlsrv_fetch_array($result))
|
||||
{
|
||||
$html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
|
||||
}
|
||||
}
|
||||
@sqlsrv_query($this->db_connect_id, 'SET SHOWPLAN_TEXT OFF;');
|
||||
@sqlsrv_free_stmt($result);
|
||||
|
||||
if ($html_table)
|
||||
{
|
||||
$this->html_hold .= '</table>';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'fromcache':
|
||||
$endtime = explode(' ', microtime());
|
||||
$endtime = $endtime[0] + $endtime[1];
|
||||
|
||||
$result = @sqlsrv_query($this->db_connect_id, $query);
|
||||
while ($void = @sqlsrv_fetch_array($result))
|
||||
{
|
||||
// Take the time spent on parsing rows into account
|
||||
}
|
||||
@sqlsrv_free_stmt($result);
|
||||
|
||||
$splittime = explode(' ', microtime());
|
||||
$splittime = $splittime[0] + $splittime[1];
|
||||
|
||||
$this->sql_report('record_fromcache', $query, $endtime, $splittime);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method used to retrieve number of rows
|
||||
* Emulates mysql_num_rows
|
||||
* Used in acp_database.php -> write_data_mssqlnative()
|
||||
* Requires a static or keyset cursor to be definde via
|
||||
* mssqlnative_set_query_options()
|
||||
*/
|
||||
function mssqlnative_num_rows($res)
|
||||
{
|
||||
if ($res !== false)
|
||||
{
|
||||
return sqlsrv_num_rows($res);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows setting mssqlnative specific query options passed to sqlsrv_query as 4th parameter.
|
||||
*/
|
||||
function mssqlnative_set_query_options($options)
|
||||
{
|
||||
$this->query_options = $options;
|
||||
}
|
||||
}
|
565
phpBB/includes/db/driver/mysql.php
Normal file
565
phpBB/includes/db/driver/mysql.php
Normal file
@@ -0,0 +1,565 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @package dbal
|
||||
* @copyright (c) 2005 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
if (!defined('IN_PHPBB'))
|
||||
{
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* MySQL4 Database Abstraction Layer
|
||||
* Compatible with:
|
||||
* MySQL 3.23+
|
||||
* MySQL 4.0+
|
||||
* MySQL 4.1+
|
||||
* MySQL 5.0+
|
||||
* @package dbal
|
||||
*/
|
||||
class phpbb_db_driver_mysql extends phpbb_db_driver
|
||||
{
|
||||
var $multi_insert = true;
|
||||
|
||||
/**
|
||||
* Connect to server
|
||||
* @access public
|
||||
*/
|
||||
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
|
||||
{
|
||||
$this->persistency = $persistency;
|
||||
$this->user = $sqluser;
|
||||
$this->server = $sqlserver . (($port) ? ':' . $port : '');
|
||||
$this->dbname = $database;
|
||||
|
||||
$this->sql_layer = 'mysql4';
|
||||
|
||||
$this->db_connect_id = ($this->persistency) ? @mysql_pconnect($this->server, $this->user, $sqlpassword) : @mysql_connect($this->server, $this->user, $sqlpassword, $new_link);
|
||||
|
||||
if ($this->db_connect_id && $this->dbname != '')
|
||||
{
|
||||
if (@mysql_select_db($this->dbname, $this->db_connect_id))
|
||||
{
|
||||
// Determine what version we are using and if it natively supports UNICODE
|
||||
if (version_compare($this->sql_server_info(true), '4.1.0', '>='))
|
||||
{
|
||||
@mysql_query("SET NAMES 'utf8'", $this->db_connect_id);
|
||||
|
||||
// enforce strict mode on databases that support it
|
||||
if (version_compare($this->sql_server_info(true), '5.0.2', '>='))
|
||||
{
|
||||
$result = @mysql_query('SELECT @@session.sql_mode AS sql_mode', $this->db_connect_id);
|
||||
$row = @mysql_fetch_assoc($result);
|
||||
@mysql_free_result($result);
|
||||
$modes = array_map('trim', explode(',', $row['sql_mode']));
|
||||
|
||||
// TRADITIONAL includes STRICT_ALL_TABLES and STRICT_TRANS_TABLES
|
||||
if (!in_array('TRADITIONAL', $modes))
|
||||
{
|
||||
if (!in_array('STRICT_ALL_TABLES', $modes))
|
||||
{
|
||||
$modes[] = 'STRICT_ALL_TABLES';
|
||||
}
|
||||
|
||||
if (!in_array('STRICT_TRANS_TABLES', $modes))
|
||||
{
|
||||
$modes[] = 'STRICT_TRANS_TABLES';
|
||||
}
|
||||
}
|
||||
|
||||
$mode = implode(',', $modes);
|
||||
@mysql_query("SET SESSION sql_mode='{$mode}'", $this->db_connect_id);
|
||||
}
|
||||
}
|
||||
else if (version_compare($this->sql_server_info(true), '4.0.0', '<'))
|
||||
{
|
||||
$this->sql_layer = 'mysql';
|
||||
}
|
||||
|
||||
return $this->db_connect_id;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->sql_error('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Version information about used database
|
||||
* @param bool $raw if true, only return the fetched sql_server_version
|
||||
* @param bool $use_cache If true, it is safe to retrieve the value from the cache
|
||||
* @return string sql server version
|
||||
*/
|
||||
function sql_server_info($raw = false, $use_cache = true)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('mysql_version')) === false)
|
||||
{
|
||||
$result = @mysql_query('SELECT VERSION() AS version', $this->db_connect_id);
|
||||
$row = @mysql_fetch_assoc($result);
|
||||
@mysql_free_result($result);
|
||||
|
||||
$this->sql_server_version = $row['version'];
|
||||
|
||||
if (!empty($cache) && $use_cache)
|
||||
{
|
||||
$cache->put('mysql_version', $this->sql_server_version);
|
||||
}
|
||||
}
|
||||
|
||||
return ($raw) ? $this->sql_server_version : 'MySQL ' . $this->sql_server_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function sql_concatenate($expr1, $expr2)
|
||||
{
|
||||
return 'CONCAT(' . $expr1 . ', ' . $expr2 . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL Transaction
|
||||
* @access private
|
||||
*/
|
||||
function _sql_transaction($status = 'begin')
|
||||
{
|
||||
switch ($status)
|
||||
{
|
||||
case 'begin':
|
||||
return @mysql_query('BEGIN', $this->db_connect_id);
|
||||
break;
|
||||
|
||||
case 'commit':
|
||||
return @mysql_query('COMMIT', $this->db_connect_id);
|
||||
break;
|
||||
|
||||
case 'rollback':
|
||||
return @mysql_query('ROLLBACK', $this->db_connect_id);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base query method
|
||||
*
|
||||
* @param string $query Contains the SQL query which shall be executed
|
||||
* @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
|
||||
* @return mixed When casted to bool the returned value returns true on success and false on failure
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function sql_query($query = '', $cache_ttl = 0)
|
||||
{
|
||||
if ($query != '')
|
||||
{
|
||||
global $cache;
|
||||
|
||||
// EXPLAIN only in extra debug mode
|
||||
if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('start', $query);
|
||||
}
|
||||
|
||||
$this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
|
||||
$this->sql_add_num_queries($this->query_result);
|
||||
|
||||
if ($this->query_result === false)
|
||||
{
|
||||
if (($this->query_result = @mysql_query($query, $this->db_connect_id)) === false)
|
||||
{
|
||||
$this->sql_error($query);
|
||||
}
|
||||
|
||||
if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('stop', $query);
|
||||
}
|
||||
|
||||
if ($cache_ttl && method_exists($cache, 'sql_save'))
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
$cache->sql_save($query, $this->query_result, $cache_ttl);
|
||||
}
|
||||
else if (strpos($query, 'SELECT') === 0 && $this->query_result)
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
}
|
||||
}
|
||||
else if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('fromcache', $query);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->query_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIMIT query
|
||||
*/
|
||||
function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
|
||||
{
|
||||
$this->query_result = false;
|
||||
|
||||
// if $total is set to 0 we do not want to limit the number of rows
|
||||
if ($total == 0)
|
||||
{
|
||||
// Having a value of -1 was always a bug
|
||||
$total = '18446744073709551615';
|
||||
}
|
||||
|
||||
$query .= "\n LIMIT " . ((!empty($offset)) ? $offset . ', ' . $total : $total);
|
||||
|
||||
return $this->sql_query($query, $cache_ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return number of affected rows
|
||||
*/
|
||||
function sql_affectedrows()
|
||||
{
|
||||
return ($this->db_connect_id) ? @mysql_affected_rows($this->db_connect_id) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch current row
|
||||
*/
|
||||
function sql_fetchrow($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_fetchrow($query_id);
|
||||
}
|
||||
|
||||
return ($query_id !== false) ? @mysql_fetch_assoc($query_id) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek to given row number
|
||||
* rownum is zero-based
|
||||
*/
|
||||
function sql_rowseek($rownum, &$query_id)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_rowseek($rownum, $query_id);
|
||||
}
|
||||
|
||||
return ($query_id !== false) ? @mysql_data_seek($query_id, $rownum) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last inserted id after insert statement
|
||||
*/
|
||||
function sql_nextid()
|
||||
{
|
||||
return ($this->db_connect_id) ? @mysql_insert_id($this->db_connect_id) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free sql result
|
||||
*/
|
||||
function sql_freeresult($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_freeresult($query_id);
|
||||
}
|
||||
|
||||
if (isset($this->open_queries[(int) $query_id]))
|
||||
{
|
||||
unset($this->open_queries[(int) $query_id]);
|
||||
return @mysql_free_result($query_id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape string used in sql query
|
||||
*/
|
||||
function sql_escape($msg)
|
||||
{
|
||||
if (!$this->db_connect_id)
|
||||
{
|
||||
return @mysql_real_escape_string($msg);
|
||||
}
|
||||
|
||||
return @mysql_real_escape_string($msg, $this->db_connect_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the estimated number of rows in a specified table.
|
||||
*
|
||||
* @param string $table_name Table name
|
||||
*
|
||||
* @return string Number of rows in $table_name.
|
||||
* Prefixed with ~ if estimated (otherwise exact).
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function get_estimated_row_count($table_name)
|
||||
{
|
||||
$table_status = $this->get_table_status($table_name);
|
||||
|
||||
if (isset($table_status['Engine']))
|
||||
{
|
||||
if ($table_status['Engine'] === 'MyISAM')
|
||||
{
|
||||
return $table_status['Rows'];
|
||||
}
|
||||
else if ($table_status['Engine'] === 'InnoDB' && $table_status['Rows'] > 100000)
|
||||
{
|
||||
return '~' . $table_status['Rows'];
|
||||
}
|
||||
}
|
||||
|
||||
return parent::get_row_count($table_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exact number of rows in a specified table.
|
||||
*
|
||||
* @param string $table_name Table name
|
||||
*
|
||||
* @return string Exact number of rows in $table_name.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function get_row_count($table_name)
|
||||
{
|
||||
$table_status = $this->get_table_status($table_name);
|
||||
|
||||
if (isset($table_status['Engine']) && $table_status['Engine'] === 'MyISAM')
|
||||
{
|
||||
return $table_status['Rows'];
|
||||
}
|
||||
|
||||
return parent::get_row_count($table_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets some information about the specified table.
|
||||
*
|
||||
* @param string $table_name Table name
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
function get_table_status($table_name)
|
||||
{
|
||||
$sql = "SHOW TABLE STATUS
|
||||
LIKE '" . $this->sql_escape($table_name) . "'";
|
||||
$result = $this->sql_query($sql);
|
||||
$table_status = $this->sql_fetchrow($result);
|
||||
$this->sql_freeresult($result);
|
||||
|
||||
return $table_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIKE expression
|
||||
* @access private
|
||||
*/
|
||||
function _sql_like_expression($expression)
|
||||
{
|
||||
return $expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific query data
|
||||
* @access private
|
||||
*/
|
||||
function _sql_custom_build($stage, $data)
|
||||
{
|
||||
switch ($stage)
|
||||
{
|
||||
case 'FROM':
|
||||
$data = '(' . $data . ')';
|
||||
break;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* return sql error array
|
||||
* @access private
|
||||
*/
|
||||
function _sql_error()
|
||||
{
|
||||
if (!$this->db_connect_id)
|
||||
{
|
||||
return array(
|
||||
'message' => @mysql_error(),
|
||||
'code' => @mysql_errno()
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'message' => @mysql_error($this->db_connect_id),
|
||||
'code' => @mysql_errno($this->db_connect_id)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close sql connection
|
||||
* @access private
|
||||
*/
|
||||
function _sql_close()
|
||||
{
|
||||
return @mysql_close($this->db_connect_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific report
|
||||
* @access private
|
||||
*/
|
||||
function _sql_report($mode, $query = '')
|
||||
{
|
||||
static $test_prof;
|
||||
|
||||
// current detection method, might just switch to see the existance of INFORMATION_SCHEMA.PROFILING
|
||||
if ($test_prof === null)
|
||||
{
|
||||
$test_prof = false;
|
||||
if (version_compare($this->sql_server_info(true), '5.0.37', '>=') && version_compare($this->sql_server_info(true), '5.1', '<'))
|
||||
{
|
||||
$test_prof = true;
|
||||
}
|
||||
}
|
||||
|
||||
switch ($mode)
|
||||
{
|
||||
case 'start':
|
||||
|
||||
$explain_query = $query;
|
||||
if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
|
||||
{
|
||||
$explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
|
||||
}
|
||||
else if (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
|
||||
{
|
||||
$explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
|
||||
}
|
||||
|
||||
if (preg_match('/^SELECT/', $explain_query))
|
||||
{
|
||||
$html_table = false;
|
||||
|
||||
// begin profiling
|
||||
if ($test_prof)
|
||||
{
|
||||
@mysql_query('SET profiling = 1;', $this->db_connect_id);
|
||||
}
|
||||
|
||||
if ($result = @mysql_query("EXPLAIN $explain_query", $this->db_connect_id))
|
||||
{
|
||||
while ($row = @mysql_fetch_assoc($result))
|
||||
{
|
||||
$html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
|
||||
}
|
||||
}
|
||||
@mysql_free_result($result);
|
||||
|
||||
if ($html_table)
|
||||
{
|
||||
$this->html_hold .= '</table>';
|
||||
}
|
||||
|
||||
if ($test_prof)
|
||||
{
|
||||
$html_table = false;
|
||||
|
||||
// get the last profile
|
||||
if ($result = @mysql_query('SHOW PROFILE ALL;', $this->db_connect_id))
|
||||
{
|
||||
$this->html_hold .= '<br />';
|
||||
while ($row = @mysql_fetch_assoc($result))
|
||||
{
|
||||
// make <unknown> HTML safe
|
||||
if (!empty($row['Source_function']))
|
||||
{
|
||||
$row['Source_function'] = str_replace(array('<', '>'), array('<', '>'), $row['Source_function']);
|
||||
}
|
||||
|
||||
// remove unsupported features
|
||||
foreach ($row as $key => $val)
|
||||
{
|
||||
if ($val === null)
|
||||
{
|
||||
unset($row[$key]);
|
||||
}
|
||||
}
|
||||
$html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
|
||||
}
|
||||
}
|
||||
@mysql_free_result($result);
|
||||
|
||||
if ($html_table)
|
||||
{
|
||||
$this->html_hold .= '</table>';
|
||||
}
|
||||
|
||||
@mysql_query('SET profiling = 0;', $this->db_connect_id);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'fromcache':
|
||||
$endtime = explode(' ', microtime());
|
||||
$endtime = $endtime[0] + $endtime[1];
|
||||
|
||||
$result = @mysql_query($query, $this->db_connect_id);
|
||||
while ($void = @mysql_fetch_assoc($result))
|
||||
{
|
||||
// Take the time spent on parsing rows into account
|
||||
}
|
||||
@mysql_free_result($result);
|
||||
|
||||
$splittime = explode(' ', microtime());
|
||||
$splittime = $splittime[0] + $splittime[1];
|
||||
|
||||
$this->sql_report('record_fromcache', $query, $endtime, $splittime);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
566
phpBB/includes/db/driver/mysqli.php
Normal file
566
phpBB/includes/db/driver/mysqli.php
Normal file
@@ -0,0 +1,566 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @package dbal
|
||||
* @copyright (c) 2005 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
if (!defined('IN_PHPBB'))
|
||||
{
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* MySQLi Database Abstraction Layer
|
||||
* mysqli-extension has to be compiled with:
|
||||
* MySQL 4.1+ or MySQL 5.0+
|
||||
* @package dbal
|
||||
*/
|
||||
class phpbb_db_driver_mysqli extends phpbb_db_driver
|
||||
{
|
||||
var $multi_insert = true;
|
||||
|
||||
/**
|
||||
* Connect to server
|
||||
*/
|
||||
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false , $new_link = false)
|
||||
{
|
||||
// Mysqli extension supports persistent connection since PHP 5.3.0
|
||||
$this->persistency = (version_compare(PHP_VERSION, '5.3.0', '>=')) ? $persistency : false;
|
||||
$this->user = $sqluser;
|
||||
|
||||
// If persistent connection, set dbhost to localhost when empty and prepend it with 'p:' prefix
|
||||
$this->server = ($this->persistency) ? 'p:' . (($sqlserver) ? $sqlserver : 'localhost') : $sqlserver;
|
||||
|
||||
$this->dbname = $database;
|
||||
$port = (!$port) ? NULL : $port;
|
||||
|
||||
// If port is set and it is not numeric, most likely mysqli socket is set.
|
||||
// Try to map it to the $socket parameter.
|
||||
$socket = NULL;
|
||||
if ($port)
|
||||
{
|
||||
if (is_numeric($port))
|
||||
{
|
||||
$port = (int) $port;
|
||||
}
|
||||
else
|
||||
{
|
||||
$socket = $port;
|
||||
$port = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
$this->db_connect_id = @mysqli_connect($this->server, $this->user, $sqlpassword, $this->dbname, $port, $socket);
|
||||
|
||||
if ($this->db_connect_id && $this->dbname != '')
|
||||
{
|
||||
@mysqli_query($this->db_connect_id, "SET NAMES 'utf8'");
|
||||
|
||||
// enforce strict mode on databases that support it
|
||||
if (version_compare($this->sql_server_info(true), '5.0.2', '>='))
|
||||
{
|
||||
$result = @mysqli_query($this->db_connect_id, 'SELECT @@session.sql_mode AS sql_mode');
|
||||
$row = @mysqli_fetch_assoc($result);
|
||||
@mysqli_free_result($result);
|
||||
|
||||
$modes = array_map('trim', explode(',', $row['sql_mode']));
|
||||
|
||||
// TRADITIONAL includes STRICT_ALL_TABLES and STRICT_TRANS_TABLES
|
||||
if (!in_array('TRADITIONAL', $modes))
|
||||
{
|
||||
if (!in_array('STRICT_ALL_TABLES', $modes))
|
||||
{
|
||||
$modes[] = 'STRICT_ALL_TABLES';
|
||||
}
|
||||
|
||||
if (!in_array('STRICT_TRANS_TABLES', $modes))
|
||||
{
|
||||
$modes[] = 'STRICT_TRANS_TABLES';
|
||||
}
|
||||
}
|
||||
|
||||
$mode = implode(',', $modes);
|
||||
@mysqli_query($this->db_connect_id, "SET SESSION sql_mode='{$mode}'");
|
||||
}
|
||||
return $this->db_connect_id;
|
||||
}
|
||||
|
||||
return $this->sql_error('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Version information about used database
|
||||
* @param bool $use_cache If true, it is safe to retrieve the value from the cache
|
||||
* @return string sql server version
|
||||
*/
|
||||
function sql_server_info($raw = false, $use_cache = true)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('mysqli_version')) === false)
|
||||
{
|
||||
$result = @mysqli_query($this->db_connect_id, 'SELECT VERSION() AS version');
|
||||
$row = @mysqli_fetch_assoc($result);
|
||||
@mysqli_free_result($result);
|
||||
|
||||
$this->sql_server_version = $row['version'];
|
||||
|
||||
if (!empty($cache) && $use_cache)
|
||||
{
|
||||
$cache->put('mysqli_version', $this->sql_server_version);
|
||||
}
|
||||
}
|
||||
|
||||
return ($raw) ? $this->sql_server_version : 'MySQL(i) ' . $this->sql_server_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function sql_concatenate($expr1, $expr2)
|
||||
{
|
||||
return 'CONCAT(' . $expr1 . ', ' . $expr2 . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL Transaction
|
||||
* @access private
|
||||
*/
|
||||
function _sql_transaction($status = 'begin')
|
||||
{
|
||||
switch ($status)
|
||||
{
|
||||
case 'begin':
|
||||
return @mysqli_autocommit($this->db_connect_id, false);
|
||||
break;
|
||||
|
||||
case 'commit':
|
||||
$result = @mysqli_commit($this->db_connect_id);
|
||||
@mysqli_autocommit($this->db_connect_id, true);
|
||||
return $result;
|
||||
break;
|
||||
|
||||
case 'rollback':
|
||||
$result = @mysqli_rollback($this->db_connect_id);
|
||||
@mysqli_autocommit($this->db_connect_id, true);
|
||||
return $result;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base query method
|
||||
*
|
||||
* @param string $query Contains the SQL query which shall be executed
|
||||
* @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
|
||||
* @return mixed When casted to bool the returned value returns true on success and false on failure
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function sql_query($query = '', $cache_ttl = 0)
|
||||
{
|
||||
if ($query != '')
|
||||
{
|
||||
global $cache;
|
||||
|
||||
// EXPLAIN only in extra debug mode
|
||||
if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('start', $query);
|
||||
}
|
||||
|
||||
$this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
|
||||
$this->sql_add_num_queries($this->query_result);
|
||||
|
||||
if ($this->query_result === false)
|
||||
{
|
||||
if (($this->query_result = @mysqli_query($this->db_connect_id, $query)) === false)
|
||||
{
|
||||
$this->sql_error($query);
|
||||
}
|
||||
|
||||
if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('stop', $query);
|
||||
}
|
||||
|
||||
if ($cache_ttl && method_exists($cache, 'sql_save'))
|
||||
{
|
||||
$cache->sql_save($query, $this->query_result, $cache_ttl);
|
||||
}
|
||||
}
|
||||
else if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('fromcache', $query);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->query_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIMIT query
|
||||
*/
|
||||
function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
|
||||
{
|
||||
$this->query_result = false;
|
||||
|
||||
// if $total is set to 0 we do not want to limit the number of rows
|
||||
if ($total == 0)
|
||||
{
|
||||
// MySQL 4.1+ no longer supports -1 in limit queries
|
||||
$total = '18446744073709551615';
|
||||
}
|
||||
|
||||
$query .= "\n LIMIT " . ((!empty($offset)) ? $offset . ', ' . $total : $total);
|
||||
|
||||
return $this->sql_query($query, $cache_ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return number of affected rows
|
||||
*/
|
||||
function sql_affectedrows()
|
||||
{
|
||||
return ($this->db_connect_id) ? @mysqli_affected_rows($this->db_connect_id) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch current row
|
||||
*/
|
||||
function sql_fetchrow($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (!is_object($query_id) && isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_fetchrow($query_id);
|
||||
}
|
||||
|
||||
if ($query_id !== false)
|
||||
{
|
||||
$result = @mysqli_fetch_assoc($query_id);
|
||||
return $result !== null ? $result : false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek to given row number
|
||||
* rownum is zero-based
|
||||
*/
|
||||
function sql_rowseek($rownum, &$query_id)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (!is_object($query_id) && isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_rowseek($rownum, $query_id);
|
||||
}
|
||||
|
||||
return ($query_id !== false) ? @mysqli_data_seek($query_id, $rownum) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last inserted id after insert statement
|
||||
*/
|
||||
function sql_nextid()
|
||||
{
|
||||
return ($this->db_connect_id) ? @mysqli_insert_id($this->db_connect_id) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free sql result
|
||||
*/
|
||||
function sql_freeresult($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (!is_object($query_id) && isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_freeresult($query_id);
|
||||
}
|
||||
|
||||
return @mysqli_free_result($query_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape string used in sql query
|
||||
*/
|
||||
function sql_escape($msg)
|
||||
{
|
||||
return @mysqli_real_escape_string($this->db_connect_id, $msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the estimated number of rows in a specified table.
|
||||
*
|
||||
* @param string $table_name Table name
|
||||
*
|
||||
* @return string Number of rows in $table_name.
|
||||
* Prefixed with ~ if estimated (otherwise exact).
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function get_estimated_row_count($table_name)
|
||||
{
|
||||
$table_status = $this->get_table_status($table_name);
|
||||
|
||||
if (isset($table_status['Engine']))
|
||||
{
|
||||
if ($table_status['Engine'] === 'MyISAM')
|
||||
{
|
||||
return $table_status['Rows'];
|
||||
}
|
||||
else if ($table_status['Engine'] === 'InnoDB' && $table_status['Rows'] > 100000)
|
||||
{
|
||||
return '~' . $table_status['Rows'];
|
||||
}
|
||||
}
|
||||
|
||||
return parent::get_row_count($table_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exact number of rows in a specified table.
|
||||
*
|
||||
* @param string $table_name Table name
|
||||
*
|
||||
* @return string Exact number of rows in $table_name.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function get_row_count($table_name)
|
||||
{
|
||||
$table_status = $this->get_table_status($table_name);
|
||||
|
||||
if (isset($table_status['Engine']) && $table_status['Engine'] === 'MyISAM')
|
||||
{
|
||||
return $table_status['Rows'];
|
||||
}
|
||||
|
||||
return parent::get_row_count($table_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets some information about the specified table.
|
||||
*
|
||||
* @param string $table_name Table name
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
function get_table_status($table_name)
|
||||
{
|
||||
$sql = "SHOW TABLE STATUS
|
||||
LIKE '" . $this->sql_escape($table_name) . "'";
|
||||
$result = $this->sql_query($sql);
|
||||
$table_status = $this->sql_fetchrow($result);
|
||||
$this->sql_freeresult($result);
|
||||
|
||||
return $table_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIKE expression
|
||||
* @access private
|
||||
*/
|
||||
function _sql_like_expression($expression)
|
||||
{
|
||||
return $expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific query data
|
||||
* @access private
|
||||
*/
|
||||
function _sql_custom_build($stage, $data)
|
||||
{
|
||||
switch ($stage)
|
||||
{
|
||||
case 'FROM':
|
||||
$data = '(' . $data . ')';
|
||||
break;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* return sql error array
|
||||
* @access private
|
||||
*/
|
||||
function _sql_error()
|
||||
{
|
||||
if (!$this->db_connect_id)
|
||||
{
|
||||
return array(
|
||||
'message' => @mysqli_connect_error(),
|
||||
'code' => @mysqli_connect_errno()
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'message' => @mysqli_error($this->db_connect_id),
|
||||
'code' => @mysqli_errno($this->db_connect_id)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close sql connection
|
||||
* @access private
|
||||
*/
|
||||
function _sql_close()
|
||||
{
|
||||
return @mysqli_close($this->db_connect_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific report
|
||||
* @access private
|
||||
*/
|
||||
function _sql_report($mode, $query = '')
|
||||
{
|
||||
static $test_prof;
|
||||
|
||||
// current detection method, might just switch to see the existance of INFORMATION_SCHEMA.PROFILING
|
||||
if ($test_prof === null)
|
||||
{
|
||||
$test_prof = false;
|
||||
if (strpos(mysqli_get_server_info($this->db_connect_id), 'community') !== false)
|
||||
{
|
||||
$ver = mysqli_get_server_version($this->db_connect_id);
|
||||
if ($ver >= 50037 && $ver < 50100)
|
||||
{
|
||||
$test_prof = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch ($mode)
|
||||
{
|
||||
case 'start':
|
||||
|
||||
$explain_query = $query;
|
||||
if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
|
||||
{
|
||||
$explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
|
||||
}
|
||||
else if (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
|
||||
{
|
||||
$explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
|
||||
}
|
||||
|
||||
if (preg_match('/^SELECT/', $explain_query))
|
||||
{
|
||||
$html_table = false;
|
||||
|
||||
// begin profiling
|
||||
if ($test_prof)
|
||||
{
|
||||
@mysqli_query($this->db_connect_id, 'SET profiling = 1;');
|
||||
}
|
||||
|
||||
if ($result = @mysqli_query($this->db_connect_id, "EXPLAIN $explain_query"))
|
||||
{
|
||||
while ($row = @mysqli_fetch_assoc($result))
|
||||
{
|
||||
$html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
|
||||
}
|
||||
}
|
||||
@mysqli_free_result($result);
|
||||
|
||||
if ($html_table)
|
||||
{
|
||||
$this->html_hold .= '</table>';
|
||||
}
|
||||
|
||||
if ($test_prof)
|
||||
{
|
||||
$html_table = false;
|
||||
|
||||
// get the last profile
|
||||
if ($result = @mysqli_query($this->db_connect_id, 'SHOW PROFILE ALL;'))
|
||||
{
|
||||
$this->html_hold .= '<br />';
|
||||
while ($row = @mysqli_fetch_assoc($result))
|
||||
{
|
||||
// make <unknown> HTML safe
|
||||
if (!empty($row['Source_function']))
|
||||
{
|
||||
$row['Source_function'] = str_replace(array('<', '>'), array('<', '>'), $row['Source_function']);
|
||||
}
|
||||
|
||||
// remove unsupported features
|
||||
foreach ($row as $key => $val)
|
||||
{
|
||||
if ($val === null)
|
||||
{
|
||||
unset($row[$key]);
|
||||
}
|
||||
}
|
||||
$html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
|
||||
}
|
||||
}
|
||||
@mysqli_free_result($result);
|
||||
|
||||
if ($html_table)
|
||||
{
|
||||
$this->html_hold .= '</table>';
|
||||
}
|
||||
|
||||
@mysqli_query($this->db_connect_id, 'SET profiling = 0;');
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'fromcache':
|
||||
$endtime = explode(' ', microtime());
|
||||
$endtime = $endtime[0] + $endtime[1];
|
||||
|
||||
$result = @mysqli_query($this->db_connect_id, $query);
|
||||
while ($void = @mysqli_fetch_assoc($result))
|
||||
{
|
||||
// Take the time spent on parsing rows into account
|
||||
}
|
||||
@mysqli_free_result($result);
|
||||
|
||||
$splittime = explode(' ', microtime());
|
||||
$splittime = $splittime[0] + $splittime[1];
|
||||
|
||||
$this->sql_report('record_fromcache', $query, $endtime, $splittime);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
766
phpBB/includes/db/driver/oracle.php
Normal file
766
phpBB/includes/db/driver/oracle.php
Normal file
@@ -0,0 +1,766 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @package dbal
|
||||
* @copyright (c) 2005 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
if (!defined('IN_PHPBB'))
|
||||
{
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Oracle Database Abstraction Layer
|
||||
* @package dbal
|
||||
*/
|
||||
class phpbb_db_driver_oracle extends phpbb_db_driver
|
||||
{
|
||||
var $last_query_text = '';
|
||||
|
||||
/**
|
||||
* Connect to server
|
||||
*/
|
||||
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
|
||||
{
|
||||
$this->persistency = $persistency;
|
||||
$this->user = $sqluser;
|
||||
$this->server = $sqlserver . (($port) ? ':' . $port : '');
|
||||
$this->dbname = $database;
|
||||
|
||||
$connect = $database;
|
||||
|
||||
// support for "easy connect naming"
|
||||
if ($sqlserver !== '' && $sqlserver !== '/')
|
||||
{
|
||||
if (substr($sqlserver, -1, 1) == '/')
|
||||
{
|
||||
$sqlserver == substr($sqlserver, 0, -1);
|
||||
}
|
||||
$connect = $sqlserver . (($port) ? ':' . $port : '') . '/' . $database;
|
||||
}
|
||||
|
||||
$this->db_connect_id = ($new_link) ? @ocinlogon($this->user, $sqlpassword, $connect, 'UTF8') : (($this->persistency) ? @ociplogon($this->user, $sqlpassword, $connect, 'UTF8') : @ocilogon($this->user, $sqlpassword, $connect, 'UTF8'));
|
||||
|
||||
return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Version information about used database
|
||||
* @param bool $raw if true, only return the fetched sql_server_version
|
||||
* @param bool $use_cache forced to false for Oracle
|
||||
* @return string sql server version
|
||||
*/
|
||||
function sql_server_info($raw = false, $use_cache = true)
|
||||
{
|
||||
/**
|
||||
* force $use_cache false. I didn't research why the caching code below is commented out
|
||||
* but I assume its because the Oracle extension provides a direct method to access it
|
||||
* without a query.
|
||||
*/
|
||||
|
||||
$use_cache = false;
|
||||
/*
|
||||
global $cache;
|
||||
|
||||
if (empty($cache) || ($this->sql_server_version = $cache->get('oracle_version')) === false)
|
||||
{
|
||||
$result = @ociparse($this->db_connect_id, 'SELECT * FROM v$version WHERE banner LIKE \'Oracle%\'');
|
||||
@ociexecute($result, OCI_DEFAULT);
|
||||
@ocicommit($this->db_connect_id);
|
||||
|
||||
$row = array();
|
||||
@ocifetchinto($result, $row, OCI_ASSOC + OCI_RETURN_NULLS);
|
||||
@ocifreestatement($result);
|
||||
$this->sql_server_version = trim($row['BANNER']);
|
||||
|
||||
$cache->put('oracle_version', $this->sql_server_version);
|
||||
}
|
||||
*/
|
||||
$this->sql_server_version = @ociserverversion($this->db_connect_id);
|
||||
|
||||
return $this->sql_server_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL Transaction
|
||||
* @access private
|
||||
*/
|
||||
function _sql_transaction($status = 'begin')
|
||||
{
|
||||
switch ($status)
|
||||
{
|
||||
case 'begin':
|
||||
return true;
|
||||
break;
|
||||
|
||||
case 'commit':
|
||||
return @ocicommit($this->db_connect_id);
|
||||
break;
|
||||
|
||||
case 'rollback':
|
||||
return @ocirollback($this->db_connect_id);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Oracle specific code to handle the fact that it does not compare columns properly
|
||||
* @access private
|
||||
*/
|
||||
function _rewrite_col_compare($args)
|
||||
{
|
||||
if (sizeof($args) == 4)
|
||||
{
|
||||
if ($args[2] == '=')
|
||||
{
|
||||
return '(' . $args[0] . ' OR (' . $args[1] . ' is NULL AND ' . $args[3] . ' is NULL))';
|
||||
}
|
||||
else if ($args[2] == '<>')
|
||||
{
|
||||
// really just a fancy way of saying foo <> bar or (foo is NULL XOR bar is NULL) but SQL has no XOR :P
|
||||
return '(' . $args[0] . ' OR ((' . $args[1] . ' is NULL AND ' . $args[3] . ' is NOT NULL) OR (' . $args[1] . ' is NOT NULL AND ' . $args[3] . ' is NULL)))';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->_rewrite_where($args[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Oracle specific code to handle it's lack of sanity
|
||||
* @access private
|
||||
*/
|
||||
function _rewrite_where($where_clause)
|
||||
{
|
||||
preg_match_all('/\s*(AND|OR)?\s*([\w_.()]++)\s*(?:(=|<[=>]?|>=?|LIKE)\s*((?>\'(?>[^\']++|\'\')*+\'|[\d-.()]+))|((NOT )?IN\s*\((?>\'(?>[^\']++|\'\')*+\',? ?|[\d-.]+,? ?)*+\)))/', $where_clause, $result, PREG_SET_ORDER);
|
||||
$out = '';
|
||||
foreach ($result as $val)
|
||||
{
|
||||
if (!isset($val[5]))
|
||||
{
|
||||
if ($val[4] !== "''")
|
||||
{
|
||||
$out .= $val[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
$out .= ' ' . $val[1] . ' ' . $val[2];
|
||||
if ($val[3] == '=')
|
||||
{
|
||||
$out .= ' is NULL';
|
||||
}
|
||||
else if ($val[3] == '<>')
|
||||
{
|
||||
$out .= ' is NOT NULL';
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$in_clause = array();
|
||||
$sub_exp = substr($val[5], strpos($val[5], '(') + 1, -1);
|
||||
$extra = false;
|
||||
preg_match_all('/\'(?>[^\']++|\'\')*+\'|[\d-.]++/', $sub_exp, $sub_vals, PREG_PATTERN_ORDER);
|
||||
$i = 0;
|
||||
foreach ($sub_vals[0] as $sub_val)
|
||||
{
|
||||
// two things:
|
||||
// 1) This determines if an empty string was in the IN clausing, making us turn it into a NULL comparison
|
||||
// 2) This fixes the 1000 list limit that Oracle has (ORA-01795)
|
||||
if ($sub_val !== "''")
|
||||
{
|
||||
$in_clause[(int) $i++/1000][] = $sub_val;
|
||||
}
|
||||
else
|
||||
{
|
||||
$extra = true;
|
||||
}
|
||||
}
|
||||
if (!$extra && $i < 1000)
|
||||
{
|
||||
$out .= $val[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
$out .= ' ' . $val[1] . '(';
|
||||
$in_array = array();
|
||||
|
||||
// constuct each IN() clause
|
||||
foreach ($in_clause as $in_values)
|
||||
{
|
||||
$in_array[] = $val[2] . ' ' . (isset($val[6]) ? $val[6] : '') . 'IN(' . implode(', ', $in_values) . ')';
|
||||
}
|
||||
|
||||
// Join the IN() clauses against a few ORs (IN is just a nicer OR anyway)
|
||||
$out .= implode(' OR ', $in_array);
|
||||
|
||||
// handle the empty string case
|
||||
if ($extra)
|
||||
{
|
||||
$out .= ' OR ' . $val[2] . ' is ' . (isset($val[6]) ? $val[6] : '') . 'NULL';
|
||||
}
|
||||
$out .= ')';
|
||||
|
||||
unset($in_array, $in_clause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base query method
|
||||
*
|
||||
* @param string $query Contains the SQL query which shall be executed
|
||||
* @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
|
||||
* @return mixed When casted to bool the returned value returns true on success and false on failure
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function sql_query($query = '', $cache_ttl = 0)
|
||||
{
|
||||
if ($query != '')
|
||||
{
|
||||
global $cache;
|
||||
|
||||
// EXPLAIN only in extra debug mode
|
||||
if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('start', $query);
|
||||
}
|
||||
|
||||
$this->last_query_text = $query;
|
||||
$this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
|
||||
$this->sql_add_num_queries($this->query_result);
|
||||
|
||||
if ($this->query_result === false)
|
||||
{
|
||||
$in_transaction = false;
|
||||
if (!$this->transaction)
|
||||
{
|
||||
$this->sql_transaction('begin');
|
||||
}
|
||||
else
|
||||
{
|
||||
$in_transaction = true;
|
||||
}
|
||||
|
||||
$array = array();
|
||||
|
||||
// We overcome Oracle's 4000 char limit by binding vars
|
||||
if (strlen($query) > 4000)
|
||||
{
|
||||
if (preg_match('/^(INSERT INTO[^(]++)\\(([^()]+)\\) VALUES[^(]++\\((.*?)\\)$/sU', $query, $regs))
|
||||
{
|
||||
if (strlen($regs[3]) > 4000)
|
||||
{
|
||||
$cols = explode(', ', $regs[2]);
|
||||
|
||||
preg_match_all('/\'(?:[^\']++|\'\')*+\'|[\d-.]+/', $regs[3], $vals, PREG_PATTERN_ORDER);
|
||||
|
||||
/* The code inside this comment block breaks clob handling, but does allow the
|
||||
database restore script to work. If you want to allow no posts longer than 4KB
|
||||
and/or need the db restore script, uncomment this.
|
||||
|
||||
|
||||
if (sizeof($cols) !== sizeof($vals))
|
||||
{
|
||||
// Try to replace some common data we know is from our restore script or from other sources
|
||||
$regs[3] = str_replace("'||chr(47)||'", '/', $regs[3]);
|
||||
$_vals = explode(', ', $regs[3]);
|
||||
|
||||
$vals = array();
|
||||
$is_in_val = false;
|
||||
$i = 0;
|
||||
$string = '';
|
||||
|
||||
foreach ($_vals as $value)
|
||||
{
|
||||
if (strpos($value, "'") === false && !$is_in_val)
|
||||
{
|
||||
$vals[$i++] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (substr($value, -1) === "'")
|
||||
{
|
||||
$vals[$i] = $string . (($is_in_val) ? ', ' : '') . $value;
|
||||
$string = '';
|
||||
$is_in_val = false;
|
||||
|
||||
if ($vals[$i][0] !== "'")
|
||||
{
|
||||
$vals[$i] = "''" . $vals[$i];
|
||||
}
|
||||
$i++;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
$string .= (($is_in_val) ? ', ' : '') . $value;
|
||||
$is_in_val = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($string)
|
||||
{
|
||||
// New value if cols != value
|
||||
$vals[(sizeof($cols) !== sizeof($vals)) ? $i : $i - 1] .= $string;
|
||||
}
|
||||
|
||||
$vals = array(0 => $vals);
|
||||
}
|
||||
*/
|
||||
|
||||
$inserts = $vals[0];
|
||||
unset($vals);
|
||||
|
||||
foreach ($inserts as $key => $value)
|
||||
{
|
||||
if (!empty($value) && $value[0] === "'" && strlen($value) > 4002) // check to see if this thing is greater than the max + 'x2
|
||||
{
|
||||
$inserts[$key] = ':' . strtoupper($cols[$key]);
|
||||
$array[$inserts[$key]] = str_replace("''", "'", substr($value, 1, -1));
|
||||
}
|
||||
}
|
||||
|
||||
$query = $regs[1] . '(' . $regs[2] . ') VALUES (' . implode(', ', $inserts) . ')';
|
||||
}
|
||||
}
|
||||
else if (preg_match_all('/^(UPDATE [\\w_]++\\s+SET )([\\w_]++\\s*=\\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]+)(?:,\\s*[\\w_]++\\s*=\\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]+))*+)\\s+(WHERE.*)$/s', $query, $data, PREG_SET_ORDER))
|
||||
{
|
||||
if (strlen($data[0][2]) > 4000)
|
||||
{
|
||||
$update = $data[0][1];
|
||||
$where = $data[0][3];
|
||||
preg_match_all('/([\\w_]++)\\s*=\\s*(\'(?:[^\']++|\'\')*+\'|[\d-.]++)/', $data[0][2], $temp, PREG_SET_ORDER);
|
||||
unset($data);
|
||||
|
||||
$cols = array();
|
||||
foreach ($temp as $value)
|
||||
{
|
||||
if (!empty($value[2]) && $value[2][0] === "'" && strlen($value[2]) > 4002) // check to see if this thing is greater than the max + 'x2
|
||||
{
|
||||
$cols[] = $value[1] . '=:' . strtoupper($value[1]);
|
||||
$array[$value[1]] = str_replace("''", "'", substr($value[2], 1, -1));
|
||||
}
|
||||
else
|
||||
{
|
||||
$cols[] = $value[1] . '=' . $value[2];
|
||||
}
|
||||
}
|
||||
|
||||
$query = $update . implode(', ', $cols) . ' ' . $where;
|
||||
unset($cols);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (substr($query, 0, 6))
|
||||
{
|
||||
case 'DELETE':
|
||||
if (preg_match('/^(DELETE FROM [\w_]++ WHERE)((?:\s*(?:AND|OR)?\s*[\w_]+\s*(?:(?:=|<>)\s*(?>\'(?>[^\']++|\'\')*+\'|[\d-.]+)|(?:NOT )?IN\s*\((?>\'(?>[^\']++|\'\')*+\',? ?|[\d-.]+,? ?)*+\)))*+)$/', $query, $regs))
|
||||
{
|
||||
$query = $regs[1] . $this->_rewrite_where($regs[2]);
|
||||
unset($regs);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'UPDATE':
|
||||
if (preg_match('/^(UPDATE [\\w_]++\\s+SET [\\w_]+\s*=\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]++|:\w++)(?:, [\\w_]+\s*=\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]++|:\w++))*+\\s+WHERE)(.*)$/s', $query, $regs))
|
||||
{
|
||||
$query = $regs[1] . $this->_rewrite_where($regs[2]);
|
||||
unset($regs);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'SELECT':
|
||||
$query = preg_replace_callback('/([\w_.]++)\s*(?:(=|<>)\s*(?>\'(?>[^\']++|\'\')*+\'|[\d-.]++|([\w_.]++))|(?:NOT )?IN\s*\((?>\'(?>[^\']++|\'\')*+\',? ?|[\d-.]++,? ?)*+\))/', array($this, '_rewrite_col_compare'), $query);
|
||||
break;
|
||||
}
|
||||
|
||||
$this->query_result = @ociparse($this->db_connect_id, $query);
|
||||
|
||||
foreach ($array as $key => $value)
|
||||
{
|
||||
@ocibindbyname($this->query_result, $key, $array[$key], -1);
|
||||
}
|
||||
|
||||
$success = @ociexecute($this->query_result, OCI_DEFAULT);
|
||||
|
||||
if (!$success)
|
||||
{
|
||||
$this->sql_error($query);
|
||||
$this->query_result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!$in_transaction)
|
||||
{
|
||||
$this->sql_transaction('commit');
|
||||
}
|
||||
}
|
||||
|
||||
if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('stop', $query);
|
||||
}
|
||||
|
||||
if ($cache_ttl && method_exists($cache, 'sql_save'))
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
$cache->sql_save($query, $this->query_result, $cache_ttl);
|
||||
}
|
||||
else if (strpos($query, 'SELECT') === 0 && $this->query_result)
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
}
|
||||
}
|
||||
else if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('fromcache', $query);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->query_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIMIT query
|
||||
*/
|
||||
function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
|
||||
{
|
||||
$this->query_result = false;
|
||||
|
||||
$query = 'SELECT * FROM (SELECT /*+ FIRST_ROWS */ rownum AS xrownum, a.* FROM (' . $query . ') a WHERE rownum <= ' . ($offset + $total) . ') WHERE xrownum >= ' . $offset;
|
||||
|
||||
return $this->sql_query($query, $cache_ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return number of affected rows
|
||||
*/
|
||||
function sql_affectedrows()
|
||||
{
|
||||
return ($this->query_result) ? @ocirowcount($this->query_result) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch current row
|
||||
*/
|
||||
function sql_fetchrow($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_fetchrow($query_id);
|
||||
}
|
||||
|
||||
if ($query_id !== false)
|
||||
{
|
||||
$row = array();
|
||||
$result = @ocifetchinto($query_id, $row, OCI_ASSOC + OCI_RETURN_NULLS);
|
||||
|
||||
if (!$result || !$row)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$result_row = array();
|
||||
foreach ($row as $key => $value)
|
||||
{
|
||||
// Oracle treats empty strings as null
|
||||
if (is_null($value))
|
||||
{
|
||||
$value = '';
|
||||
}
|
||||
|
||||
// OCI->CLOB?
|
||||
if (is_object($value))
|
||||
{
|
||||
$value = $value->load();
|
||||
}
|
||||
|
||||
$result_row[strtolower($key)] = $value;
|
||||
}
|
||||
|
||||
return $result_row;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek to given row number
|
||||
* rownum is zero-based
|
||||
*/
|
||||
function sql_rowseek($rownum, &$query_id)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_rowseek($rownum, $query_id);
|
||||
}
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reset internal pointer
|
||||
@ociexecute($query_id, OCI_DEFAULT);
|
||||
|
||||
// We do not fetch the row for rownum == 0 because then the next resultset would be the second row
|
||||
for ($i = 0; $i < $rownum; $i++)
|
||||
{
|
||||
if (!$this->sql_fetchrow($query_id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last inserted id after insert statement
|
||||
*/
|
||||
function sql_nextid()
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
|
||||
if ($query_id !== false && $this->last_query_text != '')
|
||||
{
|
||||
if (preg_match('#^INSERT[\t\n ]+INTO[\t\n ]+([a-z0-9\_\-]+)#is', $this->last_query_text, $tablename))
|
||||
{
|
||||
$query = 'SELECT ' . $tablename[1] . '_seq.currval FROM DUAL';
|
||||
$stmt = @ociparse($this->db_connect_id, $query);
|
||||
@ociexecute($stmt, OCI_DEFAULT);
|
||||
|
||||
$temp_result = @ocifetchinto($stmt, $temp_array, OCI_ASSOC + OCI_RETURN_NULLS);
|
||||
@ocifreestatement($stmt);
|
||||
|
||||
if ($temp_result)
|
||||
{
|
||||
return $temp_array['CURRVAL'];
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free sql result
|
||||
*/
|
||||
function sql_freeresult($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_freeresult($query_id);
|
||||
}
|
||||
|
||||
if (isset($this->open_queries[(int) $query_id]))
|
||||
{
|
||||
unset($this->open_queries[(int) $query_id]);
|
||||
return @ocifreestatement($query_id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape string used in sql query
|
||||
*/
|
||||
function sql_escape($msg)
|
||||
{
|
||||
return str_replace(array("'", "\0"), array("''", ''), $msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIKE expression
|
||||
* @access private
|
||||
*/
|
||||
function _sql_like_expression($expression)
|
||||
{
|
||||
return $expression . " ESCAPE '\\'";
|
||||
}
|
||||
|
||||
function _sql_custom_build($stage, $data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
function _sql_bit_and($column_name, $bit, $compare = '')
|
||||
{
|
||||
return 'BITAND(' . $column_name . ', ' . (1 << $bit) . ')' . (($compare) ? ' ' . $compare : '');
|
||||
}
|
||||
|
||||
function _sql_bit_or($column_name, $bit, $compare = '')
|
||||
{
|
||||
return 'BITOR(' . $column_name . ', ' . (1 << $bit) . ')' . (($compare) ? ' ' . $compare : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* return sql error array
|
||||
* @access private
|
||||
*/
|
||||
function _sql_error()
|
||||
{
|
||||
$error = @ocierror();
|
||||
$error = (!$error) ? @ocierror($this->query_result) : $error;
|
||||
$error = (!$error) ? @ocierror($this->db_connect_id) : $error;
|
||||
|
||||
if ($error)
|
||||
{
|
||||
$this->last_error_result = $error;
|
||||
}
|
||||
else
|
||||
{
|
||||
$error = (isset($this->last_error_result) && $this->last_error_result) ? $this->last_error_result : array();
|
||||
}
|
||||
|
||||
return $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close sql connection
|
||||
* @access private
|
||||
*/
|
||||
function _sql_close()
|
||||
{
|
||||
return @ocilogoff($this->db_connect_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific report
|
||||
* @access private
|
||||
*/
|
||||
function _sql_report($mode, $query = '')
|
||||
{
|
||||
switch ($mode)
|
||||
{
|
||||
case 'start':
|
||||
|
||||
$html_table = false;
|
||||
|
||||
// Grab a plan table, any will do
|
||||
$sql = "SELECT table_name
|
||||
FROM USER_TABLES
|
||||
WHERE table_name LIKE '%PLAN_TABLE%'";
|
||||
$stmt = ociparse($this->db_connect_id, $sql);
|
||||
ociexecute($stmt);
|
||||
$result = array();
|
||||
|
||||
if (ocifetchinto($stmt, $result, OCI_ASSOC + OCI_RETURN_NULLS))
|
||||
{
|
||||
$table = $result['TABLE_NAME'];
|
||||
|
||||
// This is the statement_id that will allow us to track the plan
|
||||
$statement_id = substr(md5($query), 0, 30);
|
||||
|
||||
// Remove any stale plans
|
||||
$stmt2 = ociparse($this->db_connect_id, "DELETE FROM $table WHERE statement_id='$statement_id'");
|
||||
ociexecute($stmt2);
|
||||
ocifreestatement($stmt2);
|
||||
|
||||
// Explain the plan
|
||||
$sql = "EXPLAIN PLAN
|
||||
SET STATEMENT_ID = '$statement_id'
|
||||
FOR $query";
|
||||
$stmt2 = ociparse($this->db_connect_id, $sql);
|
||||
ociexecute($stmt2);
|
||||
ocifreestatement($stmt2);
|
||||
|
||||
// Get the data from the plan
|
||||
$sql = "SELECT operation, options, object_name, object_type, cardinality, cost
|
||||
FROM plan_table
|
||||
START WITH id = 0 AND statement_id = '$statement_id'
|
||||
CONNECT BY PRIOR id = parent_id
|
||||
AND statement_id = '$statement_id'";
|
||||
$stmt2 = ociparse($this->db_connect_id, $sql);
|
||||
ociexecute($stmt2);
|
||||
|
||||
$row = array();
|
||||
while (ocifetchinto($stmt2, $row, OCI_ASSOC + OCI_RETURN_NULLS))
|
||||
{
|
||||
$html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
|
||||
}
|
||||
|
||||
ocifreestatement($stmt2);
|
||||
|
||||
// Remove the plan we just made, we delete them on request anyway
|
||||
$stmt2 = ociparse($this->db_connect_id, "DELETE FROM $table WHERE statement_id='$statement_id'");
|
||||
ociexecute($stmt2);
|
||||
ocifreestatement($stmt2);
|
||||
}
|
||||
|
||||
ocifreestatement($stmt);
|
||||
|
||||
if ($html_table)
|
||||
{
|
||||
$this->html_hold .= '</table>';
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'fromcache':
|
||||
$endtime = explode(' ', microtime());
|
||||
$endtime = $endtime[0] + $endtime[1];
|
||||
|
||||
$result = @ociparse($this->db_connect_id, $query);
|
||||
$success = @ociexecute($result, OCI_DEFAULT);
|
||||
$row = array();
|
||||
|
||||
while (@ocifetchinto($result, $row, OCI_ASSOC + OCI_RETURN_NULLS))
|
||||
{
|
||||
// Take the time spent on parsing rows into account
|
||||
}
|
||||
@ocifreestatement($result);
|
||||
|
||||
$splittime = explode(' ', microtime());
|
||||
$splittime = $splittime[0] + $splittime[1];
|
||||
|
||||
$this->sql_report('record_fromcache', $query, $endtime, $splittime);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
491
phpBB/includes/db/driver/postgres.php
Normal file
491
phpBB/includes/db/driver/postgres.php
Normal file
@@ -0,0 +1,491 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @package dbal
|
||||
* @copyright (c) 2005 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
if (!defined('IN_PHPBB'))
|
||||
{
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* PostgreSQL Database Abstraction Layer
|
||||
* Minimum Requirement is Version 7.3+
|
||||
* @package dbal
|
||||
*/
|
||||
class phpbb_db_driver_postgres extends phpbb_db_driver
|
||||
{
|
||||
var $last_query_text = '';
|
||||
var $connect_error = '';
|
||||
|
||||
/**
|
||||
* Connect to server
|
||||
*/
|
||||
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
|
||||
{
|
||||
$connect_string = '';
|
||||
|
||||
if ($sqluser)
|
||||
{
|
||||
$connect_string .= "user=$sqluser ";
|
||||
}
|
||||
|
||||
if ($sqlpassword)
|
||||
{
|
||||
$connect_string .= "password=$sqlpassword ";
|
||||
}
|
||||
|
||||
if ($sqlserver)
|
||||
{
|
||||
// $sqlserver can carry a port separated by : for compatibility reasons
|
||||
// If $sqlserver has more than one : it's probably an IPv6 address.
|
||||
// In this case we only allow passing a port via the $port variable.
|
||||
if (substr_count($sqlserver, ':') === 1)
|
||||
{
|
||||
list($sqlserver, $port) = explode(':', $sqlserver);
|
||||
}
|
||||
|
||||
if ($sqlserver !== 'localhost')
|
||||
{
|
||||
$connect_string .= "host=$sqlserver ";
|
||||
}
|
||||
|
||||
if ($port)
|
||||
{
|
||||
$connect_string .= "port=$port ";
|
||||
}
|
||||
}
|
||||
|
||||
$schema = '';
|
||||
|
||||
if ($database)
|
||||
{
|
||||
$this->dbname = $database;
|
||||
if (strpos($database, '.') !== false)
|
||||
{
|
||||
list($database, $schema) = explode('.', $database);
|
||||
}
|
||||
$connect_string .= "dbname=$database";
|
||||
}
|
||||
|
||||
$this->persistency = $persistency;
|
||||
|
||||
if ($this->persistency)
|
||||
{
|
||||
if (!function_exists('pg_pconnect'))
|
||||
{
|
||||
$this->connect_error = 'pg_pconnect function does not exist, is pgsql extension installed?';
|
||||
return $this->sql_error('');
|
||||
}
|
||||
$collector = new phpbb_error_collector;
|
||||
$collector->install();
|
||||
$this->db_connect_id = (!$new_link) ? @pg_pconnect($connect_string) : @pg_pconnect($connect_string, PGSQL_CONNECT_FORCE_NEW);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!function_exists('pg_connect'))
|
||||
{
|
||||
$this->connect_error = 'pg_connect function does not exist, is pgsql extension installed?';
|
||||
return $this->sql_error('');
|
||||
}
|
||||
$collector = new phpbb_error_collector;
|
||||
$collector->install();
|
||||
$this->db_connect_id = (!$new_link) ? @pg_connect($connect_string) : @pg_connect($connect_string, PGSQL_CONNECT_FORCE_NEW);
|
||||
}
|
||||
|
||||
$collector->uninstall();
|
||||
|
||||
if ($this->db_connect_id)
|
||||
{
|
||||
if (version_compare($this->sql_server_info(true), '8.2', '>='))
|
||||
{
|
||||
$this->multi_insert = true;
|
||||
}
|
||||
|
||||
if ($schema !== '')
|
||||
{
|
||||
@pg_query($this->db_connect_id, 'SET search_path TO ' . $schema);
|
||||
}
|
||||
return $this->db_connect_id;
|
||||
}
|
||||
|
||||
$this->connect_error = $collector->format_errors();
|
||||
return $this->sql_error('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Version information about used database
|
||||
* @param bool $raw if true, only return the fetched sql_server_version
|
||||
* @param bool $use_cache If true, it is safe to retrieve the value from the cache
|
||||
* @return string sql server version
|
||||
*/
|
||||
function sql_server_info($raw = false, $use_cache = true)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('pgsql_version')) === false)
|
||||
{
|
||||
$query_id = @pg_query($this->db_connect_id, 'SELECT VERSION() AS version');
|
||||
$row = @pg_fetch_assoc($query_id, null);
|
||||
@pg_free_result($query_id);
|
||||
|
||||
$this->sql_server_version = (!empty($row['version'])) ? trim(substr($row['version'], 10)) : 0;
|
||||
|
||||
if (!empty($cache) && $use_cache)
|
||||
{
|
||||
$cache->put('pgsql_version', $this->sql_server_version);
|
||||
}
|
||||
}
|
||||
|
||||
return ($raw) ? $this->sql_server_version : 'PostgreSQL ' . $this->sql_server_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL Transaction
|
||||
* @access private
|
||||
*/
|
||||
function _sql_transaction($status = 'begin')
|
||||
{
|
||||
switch ($status)
|
||||
{
|
||||
case 'begin':
|
||||
return @pg_query($this->db_connect_id, 'BEGIN');
|
||||
break;
|
||||
|
||||
case 'commit':
|
||||
return @pg_query($this->db_connect_id, 'COMMIT');
|
||||
break;
|
||||
|
||||
case 'rollback':
|
||||
return @pg_query($this->db_connect_id, 'ROLLBACK');
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base query method
|
||||
*
|
||||
* @param string $query Contains the SQL query which shall be executed
|
||||
* @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
|
||||
* @return mixed When casted to bool the returned value returns true on success and false on failure
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function sql_query($query = '', $cache_ttl = 0)
|
||||
{
|
||||
if ($query != '')
|
||||
{
|
||||
global $cache;
|
||||
|
||||
// EXPLAIN only in extra debug mode
|
||||
if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('start', $query);
|
||||
}
|
||||
|
||||
$this->last_query_text = $query;
|
||||
$this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
|
||||
$this->sql_add_num_queries($this->query_result);
|
||||
|
||||
if ($this->query_result === false)
|
||||
{
|
||||
if (($this->query_result = @pg_query($this->db_connect_id, $query)) === false)
|
||||
{
|
||||
$this->sql_error($query);
|
||||
}
|
||||
|
||||
if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('stop', $query);
|
||||
}
|
||||
|
||||
if ($cache_ttl && method_exists($cache, 'sql_save'))
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
$cache->sql_save($query, $this->query_result, $cache_ttl);
|
||||
}
|
||||
else if (strpos($query, 'SELECT') === 0 && $this->query_result)
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
}
|
||||
}
|
||||
else if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('fromcache', $query);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->query_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific query data
|
||||
* @access private
|
||||
*/
|
||||
function _sql_custom_build($stage, $data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIMIT query
|
||||
*/
|
||||
function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
|
||||
{
|
||||
$this->query_result = false;
|
||||
|
||||
// if $total is set to 0 we do not want to limit the number of rows
|
||||
if ($total == 0)
|
||||
{
|
||||
$total = 'ALL';
|
||||
}
|
||||
|
||||
$query .= "\n LIMIT $total OFFSET $offset";
|
||||
|
||||
return $this->sql_query($query, $cache_ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return number of affected rows
|
||||
*/
|
||||
function sql_affectedrows()
|
||||
{
|
||||
return ($this->query_result) ? @pg_affected_rows($this->query_result) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch current row
|
||||
*/
|
||||
function sql_fetchrow($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_fetchrow($query_id);
|
||||
}
|
||||
|
||||
return ($query_id !== false) ? @pg_fetch_assoc($query_id, null) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek to given row number
|
||||
* rownum is zero-based
|
||||
*/
|
||||
function sql_rowseek($rownum, &$query_id)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_rowseek($rownum, $query_id);
|
||||
}
|
||||
|
||||
return ($query_id !== false) ? @pg_result_seek($query_id, $rownum) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last inserted id after insert statement
|
||||
*/
|
||||
function sql_nextid()
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
|
||||
if ($query_id !== false && $this->last_query_text != '')
|
||||
{
|
||||
if (preg_match("/^INSERT[\t\n ]+INTO[\t\n ]+([a-z0-9\_\-]+)/is", $this->last_query_text, $tablename))
|
||||
{
|
||||
$query = "SELECT currval('" . $tablename[1] . "_seq') AS last_value";
|
||||
$temp_q_id = @pg_query($this->db_connect_id, $query);
|
||||
|
||||
if (!$temp_q_id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$temp_result = @pg_fetch_assoc($temp_q_id, NULL);
|
||||
@pg_free_result($query_id);
|
||||
|
||||
return ($temp_result) ? $temp_result['last_value'] : false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free sql result
|
||||
*/
|
||||
function sql_freeresult($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_freeresult($query_id);
|
||||
}
|
||||
|
||||
if (isset($this->open_queries[(int) $query_id]))
|
||||
{
|
||||
unset($this->open_queries[(int) $query_id]);
|
||||
return @pg_free_result($query_id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape string used in sql query
|
||||
* Note: Do not use for bytea values if we may use them at a later stage
|
||||
*/
|
||||
function sql_escape($msg)
|
||||
{
|
||||
return @pg_escape_string($msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIKE expression
|
||||
* @access private
|
||||
*/
|
||||
function _sql_like_expression($expression)
|
||||
{
|
||||
return $expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
function cast_expr_to_bigint($expression)
|
||||
{
|
||||
return 'CAST(' . $expression . ' as DECIMAL(255, 0))';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
function cast_expr_to_string($expression)
|
||||
{
|
||||
return 'CAST(' . $expression . ' as VARCHAR(255))';
|
||||
}
|
||||
|
||||
/**
|
||||
* return sql error array
|
||||
* @access private
|
||||
*/
|
||||
function _sql_error()
|
||||
{
|
||||
// pg_last_error only works when there is an established connection.
|
||||
// Connection errors have to be tracked by us manually.
|
||||
if ($this->db_connect_id)
|
||||
{
|
||||
$message = @pg_last_error($this->db_connect_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
$message = $this->connect_error;
|
||||
}
|
||||
|
||||
return array(
|
||||
'message' => $message,
|
||||
'code' => ''
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close sql connection
|
||||
* @access private
|
||||
*/
|
||||
function _sql_close()
|
||||
{
|
||||
return @pg_close($this->db_connect_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific report
|
||||
* @access private
|
||||
*/
|
||||
function _sql_report($mode, $query = '')
|
||||
{
|
||||
switch ($mode)
|
||||
{
|
||||
case 'start':
|
||||
|
||||
$explain_query = $query;
|
||||
if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
|
||||
{
|
||||
$explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
|
||||
}
|
||||
else if (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
|
||||
{
|
||||
$explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
|
||||
}
|
||||
|
||||
if (preg_match('/^SELECT/', $explain_query))
|
||||
{
|
||||
$html_table = false;
|
||||
|
||||
if ($result = @pg_query($this->db_connect_id, "EXPLAIN $explain_query"))
|
||||
{
|
||||
while ($row = @pg_fetch_assoc($result, NULL))
|
||||
{
|
||||
$html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
|
||||
}
|
||||
}
|
||||
@pg_free_result($result);
|
||||
|
||||
if ($html_table)
|
||||
{
|
||||
$this->html_hold .= '</table>';
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'fromcache':
|
||||
$endtime = explode(' ', microtime());
|
||||
$endtime = $endtime[0] + $endtime[1];
|
||||
|
||||
$result = @pg_query($this->db_connect_id, $query);
|
||||
while ($void = @pg_fetch_assoc($result, NULL))
|
||||
{
|
||||
// Take the time spent on parsing rows into account
|
||||
}
|
||||
@pg_free_result($result);
|
||||
|
||||
$splittime = explode(' ', microtime());
|
||||
$splittime = $splittime[0] + $splittime[1];
|
||||
|
||||
$this->sql_report('record_fromcache', $query, $endtime, $splittime);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
334
phpBB/includes/db/driver/sqlite.php
Normal file
334
phpBB/includes/db/driver/sqlite.php
Normal file
@@ -0,0 +1,334 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @package dbal
|
||||
* @copyright (c) 2005 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
if (!defined('IN_PHPBB'))
|
||||
{
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sqlite Database Abstraction Layer
|
||||
* Minimum Requirement: 2.8.2+
|
||||
* @package dbal
|
||||
*/
|
||||
class phpbb_db_driver_sqlite extends phpbb_db_driver
|
||||
{
|
||||
/**
|
||||
* Connect to server
|
||||
*/
|
||||
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
|
||||
{
|
||||
$this->persistency = $persistency;
|
||||
$this->user = $sqluser;
|
||||
$this->server = $sqlserver . (($port) ? ':' . $port : '');
|
||||
$this->dbname = $database;
|
||||
|
||||
$error = '';
|
||||
$this->db_connect_id = ($this->persistency) ? @sqlite_popen($this->server, 0666, $error) : @sqlite_open($this->server, 0666, $error);
|
||||
|
||||
if ($this->db_connect_id)
|
||||
{
|
||||
@sqlite_query('PRAGMA short_column_names = 1', $this->db_connect_id);
|
||||
// @sqlite_query('PRAGMA encoding = "UTF-8"', $this->db_connect_id);
|
||||
}
|
||||
|
||||
return ($this->db_connect_id) ? true : array('message' => $error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Version information about used database
|
||||
* @param bool $raw if true, only return the fetched sql_server_version
|
||||
* @param bool $use_cache if true, it is safe to retrieve the stored value from the cache
|
||||
* @return string sql server version
|
||||
*/
|
||||
function sql_server_info($raw = false, $use_cache = true)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('sqlite_version')) === false)
|
||||
{
|
||||
$result = @sqlite_query('SELECT sqlite_version() AS version', $this->db_connect_id);
|
||||
$row = @sqlite_fetch_array($result, SQLITE_ASSOC);
|
||||
|
||||
$this->sql_server_version = (!empty($row['version'])) ? $row['version'] : 0;
|
||||
|
||||
if (!empty($cache) && $use_cache)
|
||||
{
|
||||
$cache->put('sqlite_version', $this->sql_server_version);
|
||||
}
|
||||
}
|
||||
|
||||
return ($raw) ? $this->sql_server_version : 'SQLite ' . $this->sql_server_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL Transaction
|
||||
* @access private
|
||||
*/
|
||||
function _sql_transaction($status = 'begin')
|
||||
{
|
||||
switch ($status)
|
||||
{
|
||||
case 'begin':
|
||||
return @sqlite_query('BEGIN', $this->db_connect_id);
|
||||
break;
|
||||
|
||||
case 'commit':
|
||||
return @sqlite_query('COMMIT', $this->db_connect_id);
|
||||
break;
|
||||
|
||||
case 'rollback':
|
||||
return @sqlite_query('ROLLBACK', $this->db_connect_id);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base query method
|
||||
*
|
||||
* @param string $query Contains the SQL query which shall be executed
|
||||
* @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
|
||||
* @return mixed When casted to bool the returned value returns true on success and false on failure
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
function sql_query($query = '', $cache_ttl = 0)
|
||||
{
|
||||
if ($query != '')
|
||||
{
|
||||
global $cache;
|
||||
|
||||
// EXPLAIN only in extra debug mode
|
||||
if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('start', $query);
|
||||
}
|
||||
|
||||
$this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
|
||||
$this->sql_add_num_queries($this->query_result);
|
||||
|
||||
if ($this->query_result === false)
|
||||
{
|
||||
if (($this->query_result = @sqlite_query($query, $this->db_connect_id)) === false)
|
||||
{
|
||||
$this->sql_error($query);
|
||||
}
|
||||
|
||||
if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('stop', $query);
|
||||
}
|
||||
|
||||
if ($cache_ttl && method_exists($cache, 'sql_save'))
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
$cache->sql_save($query, $this->query_result, $cache_ttl);
|
||||
}
|
||||
else if (strpos($query, 'SELECT') === 0 && $this->query_result)
|
||||
{
|
||||
$this->open_queries[(int) $this->query_result] = $this->query_result;
|
||||
}
|
||||
}
|
||||
else if (defined('DEBUG_EXTRA'))
|
||||
{
|
||||
$this->sql_report('fromcache', $query);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->query_result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build LIMIT query
|
||||
*/
|
||||
function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
|
||||
{
|
||||
$this->query_result = false;
|
||||
|
||||
// if $total is set to 0 we do not want to limit the number of rows
|
||||
if ($total == 0)
|
||||
{
|
||||
$total = -1;
|
||||
}
|
||||
|
||||
$query .= "\n LIMIT " . ((!empty($offset)) ? $offset . ', ' . $total : $total);
|
||||
|
||||
return $this->sql_query($query, $cache_ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return number of affected rows
|
||||
*/
|
||||
function sql_affectedrows()
|
||||
{
|
||||
return ($this->db_connect_id) ? @sqlite_changes($this->db_connect_id) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch current row
|
||||
*/
|
||||
function sql_fetchrow($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_fetchrow($query_id);
|
||||
}
|
||||
|
||||
return ($query_id !== false) ? @sqlite_fetch_array($query_id, SQLITE_ASSOC) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek to given row number
|
||||
* rownum is zero-based
|
||||
*/
|
||||
function sql_rowseek($rownum, &$query_id)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_rowseek($rownum, $query_id);
|
||||
}
|
||||
|
||||
return ($query_id !== false) ? @sqlite_seek($query_id, $rownum) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last inserted id after insert statement
|
||||
*/
|
||||
function sql_nextid()
|
||||
{
|
||||
return ($this->db_connect_id) ? @sqlite_last_insert_rowid($this->db_connect_id) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free sql result
|
||||
*/
|
||||
function sql_freeresult($query_id = false)
|
||||
{
|
||||
global $cache;
|
||||
|
||||
if ($query_id === false)
|
||||
{
|
||||
$query_id = $this->query_result;
|
||||
}
|
||||
|
||||
if (isset($cache->sql_rowset[$query_id]))
|
||||
{
|
||||
return $cache->sql_freeresult($query_id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape string used in sql query
|
||||
*/
|
||||
function sql_escape($msg)
|
||||
{
|
||||
return @sqlite_escape_string($msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Correctly adjust LIKE expression for special characters
|
||||
* For SQLite an underscore is a not-known character... this may change with SQLite3
|
||||
*/
|
||||
function sql_like_expression($expression)
|
||||
{
|
||||
// Unlike LIKE, GLOB is case sensitive (unfortunatly). SQLite users need to live with it!
|
||||
// We only catch * and ? here, not the character map possible on file globbing.
|
||||
$expression = str_replace(array(chr(0) . '_', chr(0) . '%'), array(chr(0) . '?', chr(0) . '*'), $expression);
|
||||
|
||||
$expression = str_replace(array('?', '*'), array("\?", "\*"), $expression);
|
||||
$expression = str_replace(array(chr(0) . "\?", chr(0) . "\*"), array('?', '*'), $expression);
|
||||
|
||||
return 'GLOB \'' . $this->sql_escape($expression) . '\'';
|
||||
}
|
||||
|
||||
/**
|
||||
* return sql error array
|
||||
* @access private
|
||||
*/
|
||||
function _sql_error()
|
||||
{
|
||||
return array(
|
||||
'message' => @sqlite_error_string(@sqlite_last_error($this->db_connect_id)),
|
||||
'code' => @sqlite_last_error($this->db_connect_id)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific query data
|
||||
* @access private
|
||||
*/
|
||||
function _sql_custom_build($stage, $data)
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close sql connection
|
||||
* @access private
|
||||
*/
|
||||
function _sql_close()
|
||||
{
|
||||
return @sqlite_close($this->db_connect_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build db-specific report
|
||||
* @access private
|
||||
*/
|
||||
function _sql_report($mode, $query = '')
|
||||
{
|
||||
switch ($mode)
|
||||
{
|
||||
case 'start':
|
||||
break;
|
||||
|
||||
case 'fromcache':
|
||||
$endtime = explode(' ', microtime());
|
||||
$endtime = $endtime[0] + $endtime[1];
|
||||
|
||||
$result = @sqlite_query($query, $this->db_connect_id);
|
||||
while ($void = @sqlite_fetch_array($result, SQLITE_ASSOC))
|
||||
{
|
||||
// Take the time spent on parsing rows into account
|
||||
}
|
||||
|
||||
$splittime = explode(' ', microtime());
|
||||
$splittime = $splittime[0] + $splittime[1];
|
||||
|
||||
$this->sql_report('record_fromcache', $query, $endtime, $splittime);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user