Index: branches/5.2.x/core/kernel/db/db_connection.php =================================================================== diff -u -N -r14585 -r14609 --- branches/5.2.x/core/kernel/db/db_connection.php (.../db_connection.php) (revision 14585) +++ branches/5.2.x/core/kernel/db/db_connection.php (.../db_connection.php) (revision 14609) @@ -1,6 +1,6 @@ '', 'user' => '', 'pass' => '', 'db' => ''); + protected $connectionParams = Array ('host' => '', 'user' => '', 'pass' => '', 'db' => ''); /** - * Handle of currenty processed recordset + * Index of database server * + * @var int + * @access protected + */ + protected $serverIndex = 0; + + /** + * Handle of currently processed recordset + * * @var resource - * @access private + * @access protected */ - var $queryID = null; + protected $queryID = null; /** * DB type specific function mappings * * @var Array - * @access private + * @access protected */ - var $metaFunctions = Array(); + protected $metaFunctions = Array (); /** * Function to handle sql errors * - * @var string - * @access private + * @var Array|string + * @access public */ - var $errorHandler = ''; + public $errorHandler = ''; /** * Error code * * @var int - * @access private + * @access protected */ - var $errorCode = 0; + protected $errorCode = 0; /** * Error message * * @var string - * @access private + * @access protected */ - var $errorMessage = ''; + protected $errorMessage = ''; /** * Defines if database connection * operations should generate debug * information * * @var bool + * @access public */ - var $debugMode = false; + public $debugMode = false; /** * Save query execution statistics * * @var bool + * @access protected */ - var $_captureStatistics = false; + protected $_captureStatistics = false; /** * Last query to database * * @var string + * @access public */ - var $lastQuery = ''; + public $lastQuery = ''; /** * Total processed queries count * * @var int + * @access protected */ - var $_queryCount = 0; + protected $_queryCount = 0; /** * Total time, used for serving queries * * @var Array + * @access protected */ - var $_queryTime = 0; + protected $_queryTime = 0; /** * Indicates, that next database query could be cached, when memory caching is enabled * * @var bool + * @access public */ - var $nextQueryCachable = false; + public $nextQueryCachable = false; /** + * For backwards compatibility with kDBLoadBalancer class + * + * @var bool + * @access public + */ + public $nextQueryFromMaster = false; + + /** * Initializes connection class with * db type to used in future * * @param string $dbType - * @return DBConnection + * @param string $errorHandler + * @param int $server_index * @access public */ - public function __construct($dbType, $errorHandler = '') + public function __construct($dbType, $errorHandler = '', $server_index = 0) { if ( class_exists('kApplication') ) { // prevents "Fatal Error" on 2nd installation step (when database is empty) parent::__construct(); - } + } $this->dbType = $dbType; + $this->serverIndex = $server_index; + // $this->initMetaFunctions(); if (!$errorHandler) { $this->errorHandler = Array(&$this, 'handleError'); @@ -159,9 +193,9 @@ * * @param int $code * @param string $msg - * @access public + * @access protected */ - function setError($code, $msg) + protected function setError($code, $msg) { $this->errorCode = $code; $this->errorMessage = $msg; @@ -174,7 +208,7 @@ * @return bool * @access public */ - function hasError() + public function hasError() { return $this->errorCode != 0; } @@ -183,38 +217,37 @@ * Caches function specific to requested * db type * - * @access private + * @access protected */ - function initMetaFunctions() + protected function initMetaFunctions() { - $ret = Array(); - switch ($this->dbType) - { + $ret = Array (); + + switch ( $this->dbType ) { case 'mysql': - $ret = Array(); // only define functions, that name differs from "dbType_" + $ret = Array (); // only define functions, that name differs from "dbType_" break; - - } + $this->metaFunctions = $ret; } /** - * Get's function for specific db type + * Gets function for specific db type * based on it's meta name * * @param string $name * @return string - * @access private + * @access protected */ - function getMetaFunction($name) + protected function getMetaFunction($name) { - /*if (!isset($this->metaFunctions[$name])) { + /*if ( !isset($this->metaFunctions[$name]) ) { $this->metaFunctions[$name] = $name; }*/ - return $this->dbType.'_'.$name; + return $this->dbType . '_' . $name; } @@ -227,9 +260,12 @@ * @param string $user * @param string $pass * @param string $db + * @param bool $force_new + * @param bool $retry + * @return bool * @access public */ - function Connect($host, $user, $pass, $db, $force_new = false, $retry = false) + public function Connect($host, $user, $pass, $db, $force_new = false, $retry = false) { $this->connectionParams = Array ('host' => $host, 'user' => $user, 'pass' => $pass, 'db' => $db); @@ -253,7 +289,9 @@ $func = $this->getMetaFunction('errno'); $this->errorCode = $this->connectionID ? $func($this->connectionID) : $func(); - if ( !$this->hasError() ) { + if ( is_resource($this->connectionID) && !$this->hasError() ) { + $this->connectionOpened = true; + return true; } @@ -268,17 +306,48 @@ throw new Exception($error_msg); } + $this->connectionOpened = false; + return false; } - function ReConnect($force_new = false) + /** + * Setups the connection according given configuration + * + * @param Array $config + * @return bool + * @access public + */ + public function setup($config) { + if ( is_object($this->Application) ) { + $this->debugMode = $this->Application->isDebugMode(); + } + + return $this->Connect( + $config['Database']['DBHost'], + $config['Database']['DBUser'], + $config['Database']['DBUserPassword'], + $config['Database']['DBName'] + ); + } + + /** + * Performs 3 reconnect attempts in case if connection to a DB was lost in the middle of script run (e.g. server restart) + * + * @param bool $force_new + * @return bool + * @access protected + */ + protected function ReConnect($force_new = false) + { $retry_count = 0; + $connected = false; $func = $this->getMetaFunction('close'); $func($this->connectionID); - while ($retry_count < 3) { + while ( $retry_count < 3 ) { sleep(5); // wait 5 seconds before each reconnect attempt $connected = $this->Connect( @@ -289,7 +358,7 @@ $force_new, true ); - if ($connected) { + if ( $connected ) { break; } @@ -303,12 +372,16 @@ * Shows error message from previous operation * if it failed * - * @access private + * @param string $sql + * @param string $key_field + * @param bool $no_debug + * @return bool + * @access protected */ - function showError($sql = '', $key_field = null, $no_debug = false) + protected function showError($sql = '', $key_field = null, $no_debug = false) { static $retry_count = 0; - + $func = $this->getMetaFunction('errno'); if (!$this->connectionID) { @@ -359,7 +432,14 @@ return false; } - function callErrorHandler($sql) + /** + * Sends db error to a predefined error handler + * + * @param $sql + * @return bool + * @access protected + */ + protected function callErrorHandler($sql) { if (is_array($this->errorHandler)) { $func = $this->errorHandler[1]; @@ -380,9 +460,9 @@ * @param string $msg * @param string $sql * @return bool - * @access private + * @access public */ - function handleError($code, $msg, $sql) + public function handleError($code, $msg, $sql) { echo 'Processing SQL: ' . $sql . '
'; echo 'Error (' . $code . '): ' . $msg . '
'; @@ -396,9 +476,9 @@ * * @param string $new_name * @return bool - * @access public + * @access protected */ - function setDB($new_name) + protected function setDB($new_name) { if (!$this->connectionID) return false; $func = $this->getMetaFunction('select_db'); @@ -415,11 +495,14 @@ * @return string * @access public */ - function GetOne($sql, $offset = 0) + public function GetOne($sql, $offset = 0) { $row = $this->GetRow($sql, $offset); - if(!$row) return false; + if ( !$row ) { + return false; + } + return array_shift($row); } @@ -432,12 +515,15 @@ * @return Array * @access public */ - function GetRow($sql, $offset = 0) + public function GetRow($sql, $offset = 0) { - $sql .= ' '.$this->getLimitClause($offset, 1); + $sql .= ' ' . $this->getLimitClause($offset, 1); $ret = $this->Query($sql); - if(!$ret) return false; + if ( !$ret ) { + return false; + } + return array_shift($ret); } @@ -453,25 +539,30 @@ * @return Array * @access public */ - function GetCol($sql, $key_field = null) + public function GetCol($sql, $key_field = null) { $rows = $this->Query($sql); - if (!$rows) return $rows; + if ( !$rows ) { + return $rows; + } - $i = 0; $row_count = count($rows); - $ret = Array(); - if (isset($key_field)) { - while ($i < $row_count) { + $i = 0; + $row_count = count($rows); + $ret = Array (); + + if ( isset($key_field) ) { + while ( $i < $row_count ) { $ret[$rows[$i][$key_field]] = array_shift($rows[$i]); $i++; } } else { - while ($i < $row_count) { + while ( $i < $row_count ) { $ret[] = array_shift($rows[$i]); $i++; } } + return $ret; } @@ -484,46 +575,48 @@ * * @param string $sql * @param string $key_field + * @param bool $no_debug * @return Array + * @access public */ - function Query($sql, $key_field = null, $no_debug = false) + public function Query($sql, $key_field = null, $no_debug = false) { $this->lastQuery = $sql; - if (!$no_debug) { + if ( !$no_debug ) { $this->_queryCount++; } - if ($this->debugMode && !$no_debug) { - return $this->debugQuery($sql,$key_field); + if ( $this->debugMode && !$no_debug ) { + return $this->debugQuery($sql, $key_field); } $query_func = $this->getMetaFunction('query'); // set 1st checkpoint: begin - if ($this->_captureStatistics) { + if ( $this->_captureStatistics ) { $start_time = microtime(true); } // set 1st checkpoint: end $this->setError(0, ''); // reset error - $this->queryID = $query_func($sql,$this->connectionID); - if (is_resource($this->queryID)) { - $ret = Array(); + $this->queryID = $query_func($sql, $this->connectionID); + if ( is_resource($this->queryID) ) { + $ret = Array (); $fetch_func = $this->getMetaFunction('fetch_assoc'); - if (isset($key_field)) { - while (($row = $fetch_func($this->queryID))) { + if ( isset($key_field) ) { + while ( ($row = $fetch_func($this->queryID)) ) { $ret[$row[$key_field]] = $row; } } else { - while (($row = $fetch_func($this->queryID))) { + while ( ($row = $fetch_func($this->queryID)) ) { $ret[] = $row; } } // set 2nd checkpoint: begin - if ($this->_captureStatistics) { + if ( $this->_captureStatistics ) { $query_time = microtime(true) - $start_time; - if ($query_time > DBG_MAX_SQL_TIME && !$no_debug) { + if ( $query_time > DBG_MAX_SQL_TIME && !$no_debug ) { $this->Application->logSlowQuery($sql, $query_time); } $this->_queryTime += $query_time; @@ -535,7 +628,7 @@ } else { // set 2nd checkpoint: begin - if ($this->_captureStatistics) { + if ( $this->_captureStatistics ) { $this->_queryTime += microtime(true) - $start_time; } // set 2nd checkpoint: end @@ -544,57 +637,73 @@ return $this->showError($sql, $key_field, $no_debug); } - function ChangeQuery($sql) + /** + * Performs sql query, that will change database content + * + * @param string $sql + * @return bool + * @access public + */ + public function ChangeQuery($sql) { $this->Query($sql); return $this->errorCode == 0 ? true : false; } - function debugQuery($sql, $key_field = null) + /** + * Queries db with $sql query supplied + * and returns rows selected if any, false + * otherwise. Optional parameter $key_field + * allows to set one of the query fields + * value as key in string array. + * + * Each database query will be profiled into Debugger. + * + * @param string $sql + * @param string $key_field + * @return Array + * @access public + */ + protected function debugQuery($sql, $key_field = null) { global $debugger; $query_func = $this->getMetaFunction('query'); // set 1st checkpoint: begin $profileSQLs = defined('DBG_SQL_PROFILE') && DBG_SQL_PROFILE; - if ($profileSQLs) { + if ( $profileSQLs ) { $queryID = $debugger->generateID(); - $debugger->profileStart('sql_'.$queryID, $debugger->formatSQL($sql)); + $debugger->profileStart('sql_' . $queryID, $debugger->formatSQL($sql)); } // set 1st checkpoint: end $this->setError(0, ''); // reset error $this->queryID = $query_func($sql, $this->connectionID); - if( is_resource($this->queryID) ) - { - $ret = Array(); + if ( is_resource($this->queryID) ) { + $ret = Array (); $fetch_func = $this->getMetaFunction('fetch_assoc'); - if( isset($key_field) ) - { - while( ($row = $fetch_func($this->queryID)) ) - { + if ( isset($key_field) ) { + while ( ($row = $fetch_func($this->queryID)) ) { $ret[$row[$key_field]] = $row; } } - else - { - while( ($row = $fetch_func($this->queryID)) ) - { + else { + while ( ($row = $fetch_func($this->queryID)) ) { $ret[] = $row; } } // set 2nd checkpoint: begin - if ($profileSQLs) { + if ( $profileSQLs ) { $first_cell = count($ret) == 1 && count(current($ret)) == 1 ? current(current($ret)) : null; - if (strlen($first_cell) > 200) { - $first_cell = substr($first_cell, 0, 50) . ' ...'; + if ( strlen($first_cell) > 200 ) { + $first_cell = substr($first_cell, 0, 50) . ' ...'; } - $debugger->profileFinish('sql_'.$queryID, null, null, $this->getAffectedRows(), $first_cell, $this->_queryCount, $this->nextQueryCachable); - $debugger->profilerAddTotal('sql', 'sql_'.$queryID); + $debugger->profileFinish('sql_' . $queryID, null, null, $this->getAffectedRows(), $first_cell, $this->_queryCount, $this->nextQueryCachable, $this->serverIndex); + $debugger->profilerAddTotal('sql', 'sql_' . $queryID); $this->nextQueryCachable = false; } // set 2nd checkpoint: end @@ -604,9 +713,9 @@ } else { // set 2nd checkpoint: begin - if ($profileSQLs) { - $debugger->profileFinish('sql_'.$queryID, null, null, $this->getAffectedRows(), null, $this->_queryCount, $this->nextQueryCachable); - $debugger->profilerAddTotal('sql', 'sql_'.$queryID); + if ( $profileSQLs ) { + $debugger->profileFinish('sql_' . $queryID, null, null, $this->getAffectedRows(), null, $this->_queryCount, $this->nextQueryCachable, $this->serverIndex); + $debugger->profilerAddTotal('sql', 'sql_' . $queryID); $this->nextQueryCachable = false; } // set 2nd checkpoint: end @@ -618,12 +727,11 @@ /** * Free memory used to hold recordset handle * - * @access private + * @access public */ - function Destroy() + public function Destroy() { - if($this->queryID) - { + if ( $this->queryID ) { $free_func = $this->getMetaFunction('free_result'); $free_func($this->queryID); $this->queryID = null; @@ -637,7 +745,7 @@ * @return int * @access public */ - function getInsertID() + public function getInsertID() { $func = $this->getMetaFunction('insert_id'); return $func($this->connectionID); @@ -649,7 +757,7 @@ * @return int * @access public */ - function getAffectedRows() + public function getAffectedRows() { $func = $this->getMetaFunction('affected_rows'); return $func($this->connectionID); @@ -661,16 +769,17 @@ * @param int $offset * @param int $rows * @return string - * @access private + * @access public */ - function getLimitClause($offset, $rows) + public function getLimitClause($offset, $rows) { - if(!($rows > 0)) return ''; + if ( !($rows > 0) ) { + return ''; + } - switch ($this->dbType) { - + switch ( $this->dbType ) { default: - return 'LIMIT '.$offset.','.$rows; + return 'LIMIT ' . $offset . ',' . $rows; break; } } @@ -680,10 +789,10 @@ * Otherwise returns as-is * * @param mixed $string - * * @return string + * @access public */ - function qstr($string) + public function qstr($string) { if ( is_null($string) ) { return 'NULL'; @@ -700,10 +809,10 @@ * Escapes strings (only work since PHP 4.3.0) * * @param mixed $string - * * @return string + * @access public */ - function escape($string) + public function escape($string) { if ( is_null($string) ) { return 'NULL'; @@ -716,11 +825,12 @@ } /** - * Returns last error code occured + * Returns last error code occurred * * @return int + * @access public */ - function getErrorCode() + public function getErrorCode() { return $this->errorCode; } @@ -731,7 +841,7 @@ * @return string * @access public */ - function getErrorMsg() + public function getErrorMsg() { return $this->errorMessage; } @@ -745,8 +855,9 @@ * @param string $type * @param bool $insert_now * @return bool + * @access public */ - function doInsert($fields_hash, $table, $type = 'INSERT', $insert_now = true) + public function doInsert($fields_hash, $table, $type = 'INSERT', $insert_now = true) { static $value_sqls = Array (); @@ -779,7 +890,16 @@ return $insert_result; } - function doUpdate($fields_hash, $table, $key_clause) + /** + * Update given field values to given record using $key_clause + * + * @param Array $fields_hash + * @param string $table + * @param string $key_clause + * @return bool + * @access public + */ + public function doUpdate($fields_hash, $table, $key_clause) { if (!$fields_hash) return true; @@ -797,12 +917,13 @@ } /** - * Allows to detect table's presense in database + * Allows to detect table's presence in database * * @param string $table_name * @return bool + * @access public */ - function TableFound($table_name) + public function TableFound($table_name) { static $table_found = Array(); @@ -821,9 +942,59 @@ * Returns query processing statistics * * @return Array + * @access public */ - function getQueryStatistics() + public function getQueryStatistics() { return Array ('time' => $this->_queryTime, 'count' => $this->_queryCount); } + + /** + * Get status information from SHOW STATUS in an associative array + * + * @param string $which + * @return Array + * @access public + */ + public function getStatus($which = '%') + { + $status = Array (); + $records = $this->Query('SHOW STATUS LIKE "' . $which . '"'); + + foreach ($records as $record) { + $status[ $record['Variable_name'] ] = $record['Value']; + } + + return $status; + } + + /** + * Get slave replication lag. It will only work if the DB user has the PROCESS privilege. + * + * @return int + * @access public + */ + public function getSlaveLag() + { + // don't use kDBConnection::Query method, since it will create an array of all server processes + $rs = mysql_query('SHOW PROCESSLIST', $this->connectionID); + + $skip_states = Array ( + 'Waiting for master to send event', + 'Connecting to master', + 'Queueing master event to the relay log', + 'Waiting for master update', + 'Requesting binlog dump', + ); + + // find slave SQL thread + while ( $row = mysql_fetch_array($rs) ) { + if ( $row['User'] == 'system user' && !in_array($row['State'], $skip_states) ) { + // this is it, return the time (except -ve) + return $row['Time'] > 0x7fffffff ? false : $row['Time']; + } + } + + return false; + } } \ No newline at end of file