diff options
Diffstat (limited to 'framework/Testing/Data/Distributed')
5 files changed, 1084 insertions, 0 deletions
diff --git a/framework/Testing/Data/Distributed/MasterSlave/TMasterSlaveDataSourceConfig.php b/framework/Testing/Data/Distributed/MasterSlave/TMasterSlaveDataSourceConfig.php new file mode 100644 index 00000000..ee486fe8 --- /dev/null +++ b/framework/Testing/Data/Distributed/MasterSlave/TMasterSlaveDataSourceConfig.php @@ -0,0 +1,154 @@ +<?php +/** + * TMasterSlaveDataSourceConfig class file. + * + * @author Yves Berkholz <godzilla80[at]gmx[dot]net> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2010 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Testing.Data.Distributed.MasterSlave + */ + + Prado::using('System.Testing.Data.Distributed.TDistributedDataSourceConfig'); + Prado::using('System.Testing.Data.Distributed.MasterSlave.TMasterSlaveDbConnection'); + + /** + * TMasterSlaveDataSourceConfig module class provides <module> configuration for database connections in master/slave senario. + * + * IMPORTANT!!! + * BETA Version - Use with care and NOT in production environment (only tested with MySql) + * + * Example usage: mysql connection + * <code> + * <modules> + * <module id="db1" class="System.Testing.Data.Distributed.MasterSlave.TMasterSlaveDataSourceConfig" + * ConnectionClass="System.Testing.Data.Distributed.MasterSlave.TMasterSlaveDbConnection" + * DistributedConnectionClass="System.Testing.Data.Distributed.MasterSlave.TSlaveDbConnection" + * DbConnection.StatementAnalyserClass="System.Testing.Data.Analysis.TSimpleDbStatementAnalysis"> + * <database ConnectionString="mysql:host=127.0.0.1;port=3306;dbname=mydatabase" Username="dbuser" Password="dbpass" /> + * <slave ConnectionString="mysql:host=127.0.0.1;port=3307;dbname=mydatabase" Username="dbuser" Password="dbpass" Weight="3" /> + * <slave ConnectionString="mysql:host=127.0.0.1;port=3308;dbname=mydatabase" Username="dbuser" Password="dbpass" Weight="2" /> + * <slave ConnectionString="mysql:host=127.0.0.1;port=3309;dbname=mydatabase" Username="dbuser" Password="dbpass" Weight="5" /> + * </module> + * </modules> + * </code> + * + * @author Yves Berkholz <godzilla80[at]gmx[dot]net> + * @version $Id$ + * @package System.Testing.Data.Distributed.MasterSlave + * @since 4.0 + */ + class TMasterSlaveDataSourceConfig extends TDistributedDataSourceConfig + { + /** + * @var boolean + */ + private $_bMasterInitialized = false; + + /** + * @var boolean + */ + private $_bSlaveInitialized = false; + + /** + * Constructor + */ + public function __construct() + { + $this->setConnectionClass('System.Testing.Data.Distributed.MasterSlave.TMasterSlaveDbConnection'); + $this->setDistributedConnectionClass('System.Testing.Data.Distributed.MasterSlave.TSlaveDbConnection'); + } + + /** + * Initalize the database connection properties from attributes in slave tag. + * @param TXmlDocument xml configuration. + */ + protected function initChildConnectionData($xml) + { + parent::initChildConnectionData($xml, 'slave'); + } + + /** + * @return IMasterSlaveDbConnection + */ + public function getDbConnection() { + $id = $this->getID(); + static $result = array(); + + if(!isset($result[$id])) + $result[$id] = parent::getDbConnection(); + + if(!$this->bInitialized) + return $result[$id]; + + if($this->_bMasterInitialized) + return $result[$id]; + + $this->_bMasterInitialized = true; + + if(!$result[$id] instanceof IMasterSlaveDbConnection) + return $result[$id]; + + $slave = parent::getDistributedDbConnection(); + + if($slave instanceof ISlaveDbConnection && $slave->getMasterConnection()===null) + $slave->setMasterConnection($result[$id]); + + if($result[$id]->getSlaveConnection()===null) + $result[$id]->setSlaveConnection($slave); + + return $result[$id]; + } + + /** + * @return ISlaveDbConnection + */ + public function getDistributedDbConnection() { + $id = $this->getID(); + static $result = array(); + + if(!isset($result[$id])) + $result[$id] = parent::getDistributedDbConnection(); + + if(!$this->bInitialized) + return $result[$id]; + + if($this->_bSlaveInitialized) + return $result[$id]; + + $this->_bSlaveInitialized = true; + + if(!$result[$id] instanceof ISlaveDbConnection) + return $result[$id]; + + $master = parent::getDbConnection(); + + if($master instanceof IMasterSlaveDbConnection && ($master->getSlaveConnection()===null)) + $master->setSlaveConnection($result[$id]); + + if($result[$id]->getMasterConnection()===null) + $result[$id]->setMasterConnection($master); + + return $result[$id]; + } + + /** + * Alias for getDbConnection(). + * @return IMasterSlaveDbConnection database connection. + */ + public function getMasterDbConnection() + { + return $this->getDbConnection(); + } + + /** + * Alias for getDistributedDbConnection(). + * @return ISlaveDbConnection database connection. + */ + public function getSlaveDbConnection() + { + return $this->getDistributedDbConnection(); + } + } +?>
\ No newline at end of file diff --git a/framework/Testing/Data/Distributed/MasterSlave/TMasterSlaveDbConnection.php b/framework/Testing/Data/Distributed/MasterSlave/TMasterSlaveDbConnection.php new file mode 100644 index 00000000..e6b71931 --- /dev/null +++ b/framework/Testing/Data/Distributed/MasterSlave/TMasterSlaveDbConnection.php @@ -0,0 +1,477 @@ +<?php +/** + * IMasterSlaveDbConnection, ISlaveDbConnection inferface, + * TMasterSlaveDbConnection, TSlaveDbConnection class file. + * + * @author Yves Berkholz <godzilla80[at]gmx[dot]net> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2010 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Testing.Data.Distributed.MasterSlave + * @todo Test with Backport of Yii's DBO + */ + + Prado::using('System.Data.TDbConnection'); + Prado::using('System.Testing.Data.Distributed.TDistributedDbConnection'); + Prado::using('System.Collections.TQueue'); + Prado::using('System.Collections.TStack'); + Prado::using('System.Testing.Data.Distributed.MasterSlave.TMasterSlaveDbTransaction'); + + /** + * IMasterSlaveDbConnection interface + * + * @author Yves Berkholz <godzilla80[at]gmx[dot]net> + * @version $Id$ + * @package System.Testing.Data.Distributed.MasterSlave + * @since 4.0 + */ + interface IMasterSlaveDbConnection extends IDistributedDbConnection + { + /** + * @return TQueue + */ + public function getStatementQueue(); + + /** + * @return ISlaveDbConnection|null + */ + public function getSlaveConnection(); + + /** + * @param ISlaveDbConnection|null + */ + public function setSlaveConnection($conn); + + /** + * @return TMasterSlaveDbConnectionForceMaster + */ + public function getForceMaster(); + + /** + * @param TMasterSlaveDbConnectionForceMaster + */ + public function setForceMaster($value); + + } + + /** + * ISlaveDbConnection interface + * + * @author Yves Berkholz <godzilla80[at]gmx[dot]net> + * @version $Id$ + * @package System.Testing.Data.Distributed.MasterSlave + * @since 4.0 + */ + interface ISlaveDbConnection extends IDistributedDbConnection + { + /** + * @return IMasterSlaveDbConnection|null + */ + public function getMasterConnection(); + + /** + * @param IMasterSlaveDbConnection|null + */ + public function setMasterConnection($conn); + + /** + * @return TMasterSlaveDbConnectionForceMaster + */ + public function getForceMaster(); + + /** + * @param TMasterSlaveDbConnectionForceMaster + */ + public function setForceMaster($value); + } + + /** + * TMasterSlaveDbConnection class + * + * IMPORTANT!!! + * BETA Version - Use with care and NOT in production environment (only tested with MySql) + * + * TMasterSlaveDbConnection represents a master connection to a database in master/slave senario. + * + * @author Yves Berkholz <godzilla80[at]gmx[dot]net> + * @version $Id$ + * @package System.Testing.Data.Distributed.MasterSlave + * @since 4.0 + */ + class TMasterSlaveDbConnection extends TDistributedDbConnection implements IMasterSlaveDbConnection + { + /** + * @var TSlaveDbConnection|null + */ + private $_connSlave = null; + + /** + * @var TQueue + */ + private $_statementQueue = null; + + /** + * @var integer + * @see TMasterSlaveDbConnectionForceMaster + */ + private $_forceMaster = TMasterSlaveDbConnectionForceMaster::OFF_AUTOMATIC; + + private $_forceMasterStack = null; + + /** + * Constructor. + * Note, the DB connection is not established when this connection + * instance is created. Set {@link setActive Active} property to true + * to establish the connection. + * + * @param string The Data Source Name, or DSN, contains the information required to connect to the database. + * @param string The user name for the DSN string. + * @param string The password for the DSN string. + * @param string Charset used for DB Connection (MySql & pgsql only). If not set, will use the default charset of your database server + * @see http://www.php.net/manual/en/function.PDO-construct.php + */ + public function __construct($dsn='', $username='', $password='', $charset='') + { + parent::__construct($dsn, $username, $password, $charset); + parent::setTransactionClass('System.Testing.Data.Distributed.MasterSlave.TMasterSlaveDbTransaction'); + } + + /** + * @return TQueue + */ + public function getStatementQueue() + { + if($this->_statementQueue===null) + $this->_statementQueue = new TQueue(); + return $this->_statementQueue; + } + + /** + * @return ISlaveDbConnection|null + */ + public function getSlaveConnection() + { + return $this->_connSlave; + } + + /** + * @param ISlaveDbConnection|null + * @throws TDbConnectionException if the slave connection already exists + * @throws TDbConnectionException connection not instance of ISlaveDbConnection + */ + public function setSlaveConnection($conn) + { + if($this->_connSlave !== null) + throw new TDbConnectionException('masterslavedbconnection_connection_exists', get_class($this), 'SlaveConnection'); + + if($conn!==null && !$conn instanceof ISlaveDbConnection) + throw new TDbConnectionException('masterslavedbconnection_interface_required', get_class($this), 'SlaveConnection', 'ISlaveDbConnection'); + + $this->_connSlave = $conn; + + if($this->_connSlave===null) return; + if($this->_connSlave->getMasterConnection()!==null) return; + + $this->_connSlave->setMasterConnection($this); + } + + /** + * Creates a command for execution. + * @param string SQL statement associated with the new command. + * @return TDistributedDbCommand the DB command + * @throws TDbException if the connection is not active + */ + public function createCommand($sql) + { + $force = $this->getForceMaster(); + if($force == TMasterSlaveDbConnectionForceMaster::ON_MANUAL || $force == TMasterSlaveDbConnectionForceMaster::ON_TRANSACTION) + { + Prado::log('ForceMaster: ' . $force, TLogger::DEBUG, 'System.Testing.Data.Distributed.MasterSlave.TMasterSlaveDbConnection'); + return new TDistributedDbCommand($this, $sql, TDbStatementClassification::UNKNOWN); + } + + $bEnqueue = false; + $bMaster = true; + + $classification = $this->getStatementClassification($sql); + + switch($classification) { + case TDbStatementClassification::CONTEXT: + $bEnqueue = true; + $bMaster = true; + break; + case TDbStatementClassification::SQL: + $bMaster = false; + break; + case TDbStatementClassification::TCL: + $this->setForceMaster(TMasterSlaveDbConnectionForceMaster::ON_TCL); + case TDbStatementClassification::DDL: + case TDbStatementClassification::DML: + case TDbStatementClassification::DCL: + case TDbStatementClassification::UNKNOWN: + default: + $bMaster = true; + break; + } + + $bMaster = $bMaster || $this->getForceMaster(); + + $result = new TDistributedDbCommand(($bMaster ? $this : $this->getSlaveConnection()), $sql, $classification); + //$result = new TDistributedDbCommand($this, $sql, $classification); + + if($bEnqueue) + $this->getStatementQueue()->enqueue($result); + + return $result; + } + + /** + * @return TMasterSlaveDbConnectionForceMaster + */ + public function getForceMaster() + { + return $this->_forceMaster; + } + + /** + * @param TMasterSlaveDbConnectionForceMaster + */ + public function setForceMaster($value) + { + if($this->_forceMasterStack===null) + $this->_forceMasterStack = new TStack(); + + if($value) + { + $this->_forceMaster = (integer)$value; + $this->_forceMasterStack->push((integer)$value); + } + elseif($this->_forceMasterStack->count() > 0) + $this->_forceMaster = $this->_forceMasterStack->pop(); + else + $this->_forceMaster = (integer)$value; + } + + /** + * @return TDbConnectionServerRole + */ + public function getServerRole() + { + return TDbConnectionServerRole::Master; + } + } + + /** + * TSlaveDbConnection class + * + * IMPORTANT!!! + * BETA Version - Use with care and NOT in production environment (only tested with MySql) + * + * TSlaveDbConnection represents a readonly connection to a database in master/slave senario. + * + * @author Yves Berkholz <godzilla80[at]gmx[dot]net> + * @version $Id$ + * @package System.Testing.Data.Distributed.MasterSlave + * @since 4.0 + */ + class TSlaveDbConnection extends TDbConnection implements ISlaveDbConnection + { + /** + * @var TMasterSlaveDbConnection|null + */ + private $_connMaster = null; + + /** + * @return IMasterSlaveDbConnection|null + */ + public function getMasterConnection() + { + return $this->_connMaster; + } + + /** + * @param IMasterSlaveDbConnection|null + * @throws TDbConnectionException if the master connection already exists + * @throws TDbConnectionException connection not instance of IMasterSlaveDbConnection + */ + public function setMasterConnection($conn) + { + if($this->_connMaster!==null) + throw new TDbConnectionException('masterslavedbconnection_connection_exists', get_class($this), 'MasterConnection'); + + if($conn!==null && !$conn instanceof IMasterSlaveDbConnection) + throw new TDbConnectionException('masterslavedbconnection_interface_required', get_class($this), 'MasterConnection', 'IMasterSlaveDbConnection'); + + $this->_connMaster = $conn; + } + + /** + * Creates a command for execution. + * @param string SQL statement associated with the new command. + * @return TDistributedDbCommand the DB command + * @throws TDbException if the connection is not active + */ + public function createCommand($sql) + { + $force = $this->getForceMaster(); + if($force == TMasterSlaveDbConnectionForceMaster::ON_MANUAL || $force == TMasterSlaveDbConnectionForceMaster::ON_TRANSACTION) + { + Prado::log('ForceMaster: ' . $force, TLogger::DEBUG, 'System.Testing.Data.Distributed.MasterSlave.TSlaveDbConnection'); + return new TDistributedDbCommand($this->getMasterConnection(), $sql, TDbStatementClassification::UNKNOWN); + } + + $bEnqueue = false; + $bMaster = false; + + $classification = $this->getStatementClassification($sql); + + switch($classification) { + case TDbStatementClassification::SQL: + $bMaster = false; + break; + case TDbStatementClassification::CONTEXT: + $bEnqueue = true; + $bMaster = true; + break; + case TDbStatementClassification::TCL: + $this->setForceMaster(TMasterSlaveDbConnectionForceMaster::ON_TCL); + case TDbStatementClassification::DDL: + case TDbStatementClassification::DML: + case TDbStatementClassification::DCL: + case TDbStatementClassification::UNKNOWN: + default: + $bMaster = true; + break; + } + + $bMaster = $bMaster || $this->getForceMaster(); + + $result = new TDistributedDbCommand(($bMaster ? $this->getMasterConnection() : $this), $sql, $classification); + + if($bEnqueue) + $this->getMasterConnection()->getStatementQueue()->enqueue($result); + + return $result; + } + + /** + * Starts a transaction. + * @return TDbTransaction the transaction initiated + * @throws TDbException if no master connection exists or the connection is not active + */ + public function beginTransaction() + { + if($this->getMasterConnection() === null) + throw new TDbException('slavedbconnection_requires_master', getclass($this), 'MasterConnection'); + + return $this->getMasterConnection()->beginTransaction(); + } + + /** + * @return string Transaction class name to be created by calling {@link TDbConnection::beginTransaction}. + * @throws TDbException if no master connection exists + */ + public function getTransactionClass() + { + if($this->getMasterConnection() === null) + throw new TDbException('slavedbconnection_requires_master', getclass($this), 'MasterConnection'); + return $this->getMasterConnection()->getTransactionClass(); + } + + /** + * @param string Transaction class name to be created by calling {@link TDbConnection::beginTransaction}. + * @throws TDbException if no master connection exists + */ + public function setTransactionClass($value) + { + if($this->getMasterConnection() === null) + throw new TDbException('slavedbconnection_requires_master', getclass($this), 'MasterConnection'); + $this->getMasterConnection()->setTransactionClass($value); + } + + /** + * Gets the statement analyser of type given by + * {@link setStatementAnalyserClass StatementAnalyserClass }. + * @return IDbStatementAnalysis statement analyser. + * @throws TDbException if no master connection exists + */ + public function getStatementAnalyser() + { + if($this->getMasterConnection() === null) + throw new TDbException('slavedbconnection_requires_master', getclass($this), 'MasterConnection'); + return $this->getMasterConnection()->getStatementAnalyser(); + } + + /** + * The statement analyser class name to be created when {@link getStatementAnalyserClass} + * method is called. The {@link setStatementAnalyserClass StatementAnalyserClass} + * property must be set before calling {@link getStatementAnalyser} if you wish to + * create the connection using the given class name. + * @param string Statement analyser class name. + * @throws TDbException if no master connection exists + */ + public function setStatementAnalyserClass($value) + { + if($this->getMasterConnection() === null) + throw new TDbException('slavedbconnection_requires_master', getclass($this), 'MasterConnection'); + $this->getMasterConnection()->setStatementAnalyserClass($value); + } + + /** + * @param string Statement analyser class name to be created. + * @throws TDbException if no master connection exists + */ + public function getStatementAnalyserClass() + { + if($this->getMasterConnection() === null) + throw new TDbException('slavedbconnection_requires_master', getclass($this), 'MasterConnection'); + return $this->getMasterConnection()->getStatementAnalyserClass(); + } + + /** + * @return TMasterSlaveDbConnectionForceMaster + * @throws TDbException if no master connection exists + */ + public function getForceMaster() + { + if($this->getMasterConnection() === null) + throw new TDbException('slavedbconnection_requires_master', getclass($this), 'MasterConnection'); + return $this->getMasterConnection()->getForceMaster(); + } + + /** + * @param TMasterSlaveDbConnectionForceMaster + * @throws TDbException if no master connection exists + */ + public function setForceMaster($value) + { + if($this->getMasterConnection() === null) + throw new TDbException('slavedbconnection_requires_master', getclass($this), 'MasterConnection'); + $this->getMasterConnection()->setForceMaster($value); + } + + /** + * @return TDbConnectionServerRole + */ + public function getServerRole() + { + return TDbConnectionServerRole::Slave; + } + } + + /** + * TMasterSlaveDbConnectionForceMaster class + * + * @author Yves Berkholz <godzilla80[at]gmx[dot]net> + * @version $Id$ + * @package System.Testing.Data.Distributed.MasterSlave + * @since 4.0 + */ + class TMasterSlaveDbConnectionForceMaster extends TEnumerable + { + const OFF_AUTOMATIC = 0; + const ON_MANUAL = 1; + const ON_TCL = -1; + const ON_TRANSACTION = -2; + } diff --git a/framework/Testing/Data/Distributed/MasterSlave/TMasterSlaveDbTransaction.php b/framework/Testing/Data/Distributed/MasterSlave/TMasterSlaveDbTransaction.php new file mode 100644 index 00000000..254afdb5 --- /dev/null +++ b/framework/Testing/Data/Distributed/MasterSlave/TMasterSlaveDbTransaction.php @@ -0,0 +1,83 @@ +<?php +/** + * TMasterSlaveDbTransaction class file. + * + * @author Yves Berkholz <godzilla80[at]gmx[dot]net> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2010 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Testing.Data.Distributed.MasterSlave + */ + Prado::using('System.Data.TDbTransaction'); + + /** + * TMasterSlaveDbTransaction class + * + * IMPORTANT!!! + * BETA Version - Use with care and NOT in production environment (only tested with MySql) + * + * TMasterSlaveDbTransaction represents a DB transaction in master/slave senario. + * + * @author Yves Berkholz <godzilla80[at]gmx[dot]net> + * @version $Id$ + * @package System.Testing.Data.Distributed.MasterSlave + * @since 4.0 + */ + class TMasterSlaveDbTransaction extends TDbTransaction + { + /** + * @var boolean + */ + private $_compatible = false; + + /** + * Constructor. + * @param TDbConnection the connection associated with this transaction + * @see TDbConnection::beginTransaction + */ + public function __construct(TDbConnection $connection) + { + if($connection instanceof ISlaveDbConnection) + { + $this->_compatible = true; + $master = $connection->getMasterConnection(); + $master->setForceMaster(TMasterSlaveDbConnectionForceMaster::ON_TRANSACTION); + Prado::log('contstuct, ForceMaster: ON_TRANSACTION', TLogger::DEBUG, 'System.Testing.Data.Distributed.MasterSlave.TMasterSlaveDbTransaction'); + parent::__construct($master); + } + else + { + if($connection instanceof IMasterSlaveDbConnection) + { + $this->_compatible = true; + $connection->setForceMaster(TMasterSlaveDbConnectionForceMaster::ON_TRANSACTION); + Prado::log('contstuct, ForceMaster: ON_TRANSACTION', TLogger::DEBUG, 'System.TestingData.Distributed.MasterSlave.TMasterSlaveDbTransaction'); + } + parent::__construct($connection); + } + } + + /** + * Commits a transaction. + * @throws TDbException if the transaction or the DB connection is not active. + */ + public function commit() + { + if($this->_compatible) $this->getConnection()->setForceMaster(TMasterSlaveDbConnectionForceMaster::OFF_AUTOMATIC); + Prado::log('commit, ForceMaster: OFF_AUTOMATIC', TLogger::DEBUG, 'System.Testing.Data.Distributed.MasterSlave.TMasterSlaveDbTransaction'); + parent::commit(); + } + + /** + * Rolls back a transaction. + * @throws TDbException if the transaction or the DB connection is not active. + */ + public function rollback() + { + if($this->_compatible) $this->getConnection()->setForceMaster(TMasterSlaveDbConnectionForceMaster::OFF_AUTOMATIC); + Prado::log('rollback, ForceMaster: OFF_AUTOMATIC', TLogger::DEBUG, 'System.Testing.Data.Distributed.MasterSlave.TMasterSlaveDbTransaction'); + parent::rollback(); + } + } +?>
\ No newline at end of file diff --git a/framework/Testing/Data/Distributed/TDistributedDataSourceConfig.php b/framework/Testing/Data/Distributed/TDistributedDataSourceConfig.php new file mode 100644 index 00000000..b8c4e5a9 --- /dev/null +++ b/framework/Testing/Data/Distributed/TDistributedDataSourceConfig.php @@ -0,0 +1,181 @@ +<?php +/** + * TDistributedDataSourceConfig class file. + * + * @author Yves Berkholz <godzilla80[at]gmx[dot]net> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2010 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Testing.Data.Distributed + */ + + Prado::using('System.Data.TDataSourceConfig'); + Prado::using('System.Testing.Data.Distributed.TDistributedDbConnection'); + + /** + * IDistributedDataSourceConfig module interface provides <module> configuration for database connections. + * + * @author Yves Berkholz <godzilla80[at]gmx[dot]net> + * @version $Id$ + * @package System.Testing.Data.Distributed + * @since 4.0 + */ + interface IDistributedDataSourceConfig /*extends IDataSourceConfig*/ { + /** + * @return string Database connection class name to be created for child connection. + */ + public function getDistributedConnectionClass(); + + /** + * @param string Database connection class name to be created for child connection. + */ + public function setDistributedConnectionClass($value); + } + + /** + * TDistributedDataSourceConfig module class provides <module> configuration for database connections. + * + * @author Yves Berkholz <godzilla80[at]gmx[dot]net> + * @version $Id$ + * @package System.Testing.Data.Distributed + * @since 4.0 + */ + class TDistributedDataSourceConfig extends TDataSourceConfig implements IDistributedDataSourceConfig + { + /** + * @var array + */ + private $_childConnectionAttributes = array(); + + /** + * @var string + */ + private $_connDistributedClass = 'System.Data.TDbConnection'; + + /** + * @var IDistributedDbConnection + */ + private $_connDistributed = null; + + /** + * @var boolean + */ + protected $bInitialized = false; + + /** + * @var boolean + */ + private $_hasDistributedConnectionData = false; + + /** + * Initalize the database connection properties from attributes in <database> tag. + * @param TXmlDocument xml configuration. + */ + public function init($xml) + { + parent::init($xml); + $this->initChildConnectionData($xml); + $this->bInitialized = true; + } + + /** + * Initalize the database connection properties from attributes in $tagName tag. + * @param TXmlDocument xml configuration. + * @param string Tagnames to parse. Defaults to 'child' + */ + protected function initChildConnectionData($xml, $tagName='child') + { + $c = 0; + foreach($xml->getElementsByTagName($tagName) as $item) + { + ++$c; + $this->_childConnectionAttributes[] = $item->getAttributes(); + } + + if($c===0) + throw new TConfigurationException('distributeddatasource_child_required', get_class($this), $tagName); + } + + /** + * @return string Database connection class name to be created for child connection. + */ + public function getDistributedConnectionClass() + { + return $this->_connDistributedClass; + } + + /** + * @param string Database connection class name to be created for child connection. + */ + public function setDistributedConnectionClass($value) + { + $this->_connDistributedClass=$value; + } + + /** + * @return IDistributedDbConnection + */ + public function getDistributedDbConnection() + { + $this->_hasDistributedConnectionData = false; + if($this->_connDistributed===null) + $this->_connDistributed = Prado::createComponent($this->getDistributedConnectionClass()); + + if($this->_hasDistributedConnectionData) + return $this->_connDistributed; + + $attribs = $this->getDistributedDbConnectionAttributes(); + + if($attribs===null) + return $this->_connDistributed; + + foreach($attribs as $name => $value) + $this->_connDistributed->setSubproperty($name, $value); + + $this->_hasDistributedConnectionData = true; + + return $this->_connDistributed; + } + + /** + * @return TMap + */ + protected function getDistributedDbConnectionAttributes() + { + $index = 0; + $c = count($this->_childConnectionAttributes); + + if($c > 1) { + $aSrc = array(); + $aTmp = array(); + + foreach($this->_childConnectionAttributes as $k => $item) + { + $weight = 1; + if( isset($item['Weight']) ) + $weight = $item['Weight']; + $aSrc[$k] = $weight; + } + + asort($aSrc); + + foreach($aSrc as $idx => $weight) + $aTmp = array_merge($aTmp, array_pad(array(), $weight*5, $idx)); + + $min = 0; + $max = count($aTmp)-1; + $factor = array_sum($aSrc) / count($aSrc); + $wrand = round($min + (pow(rand(0, $max) / $max, $factor) * ($max - $min))); + $index = $aTmp[$wrand]; + } + + $result = $this->_childConnectionAttributes[$index]; + + if( isset($result['Weight']) ) + unset($result['Weight']); + + return $result; + } + } +?>
\ No newline at end of file diff --git a/framework/Testing/Data/Distributed/TDistributedDbConnection.php b/framework/Testing/Data/Distributed/TDistributedDbConnection.php new file mode 100644 index 00000000..bb105d50 --- /dev/null +++ b/framework/Testing/Data/Distributed/TDistributedDbConnection.php @@ -0,0 +1,189 @@ +<?php +/** + * IDistributedDbConnection, TDistributedDbConnection inferface/class file. + * + * @author Yves Berkholz <godzilla80[at]gmx[dot]net> + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2010 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Testing.Data.Distributed + */ + + Prado::using('System.Data.TDbConnection'); + Prado::using('System.Testing.Data.Analysis.TDbStatementAnalysis'); + + /** + * TDistributedDbConnection interface + * + * @author Yves Berkholz <godzilla80[at]gmx[dot]net> + * @version $Id$ + * @package System.Testing.Data.Distributed + * @since 4.0 + */ + interface IDistributedDbConnection /*extends IDbConnection*/ + { + /** + * Gets the statement analyser of type given by + * {@link setStatementAnalyserClass StatementAnalyserClass }. + * @return IDbStatementAnalysis statement analyser. + */ + public function getStatementAnalyser(); + + /** + * The statement analyser class name to be created when {@link getStatementAnalyserClass} + * method is called. The {@link setStatementAnalyserClass StatementAnalyserClass} + * property must be set before calling {@link getStatementAnalyser} if you wish to + * create the connection using the given class name. + * @param string Statement analyser class name. + */ + public function setStatementAnalyserClass($value); + + /** + * @param string Statement analyser class name to be created. + */ + public function getStatementAnalyserClass(); + + /** + * @return TDbConnectionServerRole + */ + public function getServerRole(); + } + + /** + * TDistributedDbConnection class + * + * TDistributedDbConnection represents a conditional base connection class to a database + * + * @author Yves Berkholz <godzilla80[at]gmx[dot]net> + * @version $Id$ + * @package System.Testing.Data.Distributed + * @since 4.0 + */ + abstract class TDistributedDbConnection extends TDbConnection implements IDistributedDbConnection + { + /** + * @var string + */ + private $_statementAnalyserClass = 'System.Testing.Data.Analysis.TDbStatementAnalysis'; + + /** + * @var IDbStatementAnalysis + */ + private $_statementAnalyser = null; + + /** + * Gets the statement analyser of type given by + * {@link setStatementAnalyserClass StatementAnalyserClass }. + * @return IDbStatementAnalysis statement analyser. + */ + public function getStatementAnalyser() + { + if($this->_statementAnalyser === null) + { + $this->setActive(true); + $this->_statementAnalyser = Prado::createComponent($this->getStatementAnalyserClass()); + + if($this->getActive()) + $this->_statementAnalyser->setDriverName($this->getDriverName()); + } + return $this->_statementAnalyser; + } + + /** + * The statement analyser class name to be created when {@link getStatementAnalyser} + * method is called. The {@link setStatementAnalyserClass StatementAnalyserClass} + * property must be set before calling {@link getStatementAnalyser} if you wish to + * create the connection using the given class name. + * @param string Statement analyser class name. + */ + public function setStatementAnalyserClass($value) + { + if($this->_statementAnalyser === null) + $this->_statementAnalyserClass = $value; + } + + /** + * @param string Statement analyser class name to be created. + */ + public function getStatementAnalyserClass() + { + return $this->_statementAnalyserClass; + } + + /** + * @param string The SQL statement that should be analysed + * @param TDbStatementClassification + */ + protected function getStatementClassification($statement='', $defaultClassification=null) { + return $this->getStatementAnalyser()->getClassificationAnalysis(new TDbStatementAnalysisParameter($statement, $defaultClassification)); + } + } + + /** + * TDistributedDbCommand + * + * @author Yves Berkholz <godzilla80[at]gmx[dot]net> + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Testing.Data.Distributed + * @since 4.0 + */ + class TDistributedDbCommand extends TDbCommand + { + /** + * @var TDbStatementClassification + */ + private $_statementClassification; + + /** + * Constructor. + * @param TDbConnection the database connection + * @param string the SQL statement to be executed + * @param TDbStatementClassification Defaults to 'UNKNOWN' + */ + public function __construct(TDbConnection $connection, $text, $classification=TDbStatementClassification::UNKNOWN) + { + $connection->setActive(true); + parent::__construct($connection, $text); + $this->_statementClassification = $classification; + Prado::log($classification . ', ' . $connection->getServerRole() . ': ' . preg_replace('/[\s]+/', ' ', $text), TLogger::DEBUG, 'System.Testing.Data.Distributed.TDistributedDbCommand'); + } + + /** + * @return TDbStatementClassification + */ + public function getStatementClassification() + { + return $this->_statementClassification; + } + } + + + /** + * TDbConnectionServerRole + * + * @author Yves Berkholz <godzilla80[at]gmx[dot]net> + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Testing.Data.Distributed + * @since 4.0 + */ + class TDbConnectionServerRole extends TEnumerable + { + /** + * Master Server (Read/Write) + */ + const Master = 'Master'; + + /** + * Slave Server (Read only) + */ + const Slave = 'Slave'; + + /** + * Mirror Server (Read/Write) for further use + */ + //const Mirror = 'Mirror'; + } +?>
\ No newline at end of file |