diff options
| author | mike.benoit <mikeb@timetrex.com> | 2016-05-15 15:06:19 -0700 |
|---|---|---|
| committer | Damien Regad <dregad@mantisbt.org> | 2020-01-25 01:12:08 +0100 |
| commit | aa8384f39ab03791b0522f8670f469d47403b241 (patch) | |
| tree | 7fbb1a0d55f64a5260b9301b06d261cf939dfb83 /adodb-loadbalancer.inc.php | |
| parent | 497aa7c1de070ebd86a74981eb0d6a81e2d14900 (diff) | |
| download | adodb-aa8384f39ab03791b0522f8670f469d47403b241.tar.gz adodb-aa8384f39ab03791b0522f8670f469d47403b241.tar.bz2 adodb-aa8384f39ab03791b0522f8670f469d47403b241.zip | |
Reformat to strict PSR-2 style.
Diffstat (limited to 'adodb-loadbalancer.inc.php')
| -rw-r--r-- | adodb-loadbalancer.inc.php | 1128 |
1 files changed, 575 insertions, 553 deletions
diff --git a/adodb-loadbalancer.inc.php b/adodb-loadbalancer.inc.php index 581f1be8..e4351436 100644 --- a/adodb-loadbalancer.inc.php +++ b/adodb-loadbalancer.inc.php @@ -23,636 +23,658 @@ */ class ADOdbLoadBalancer { - /** - * @var Bool|Array All connections to each database. - */ - protected $connections = FALSE; + /** + * @var Bool|Array All connections to each database. + */ + protected $connections = false; - /** - * @var bool|Array Just connections to the master database. - */ - protected $connections_master = FALSE; + /** + * @var bool|Array Just connections to the master database. + */ + protected $connections_master = false; - /** - * @var bool|Array Just connections to the slave database. - */ - protected $connections_slave = FALSE; + /** + * @var bool|Array Just connections to the slave database. + */ + protected $connections_slave = false; - /** - * @var array Counts of all connections and their types. - */ - protected $total_connections = array('all' => 0, 'master' => 0, 'slave' => 0 ); + /** + * @var array Counts of all connections and their types. + */ + protected $total_connections = array('all' => 0, 'master' => 0, 'slave' => 0); - /** - * @var array Weights of all connections for each type. - */ - protected $total_connection_weights = array('all' => 0, 'master' => 0, 'slave' => 0 ); + /** + * @var array Weights of all connections for each type. + */ + protected $total_connection_weights = array('all' => 0, 'master' => 0, 'slave' => 0); - /** - * @var bool Once a master or slave connection is made, stick to that connection for the entire request. - */ - protected $enable_sticky_sessions = TRUE; + /** + * @var bool Once a master or slave connection is made, stick to that connection for the entire request. + */ + protected $enable_sticky_sessions = true; - /** - * @var bool When in transactions, always use this connection. - */ - protected $pinned_connection_id = FALSE; + /** + * @var bool When in transactions, always use this connection. + */ + protected $pinned_connection_id = false; - /** - * @var array Last connection_id for each database type. - */ - protected $last_connection_id = array('master' => FALSE, 'slave' => FALSE, 'all' => FALSE ); + /** + * @var array Last connection_id for each database type. + */ + protected $last_connection_id = array('master' => false, 'slave' => false, 'all' => false); - /** - * @var bool Session variables that must be maintained across all connections, ie: SET TIME ZONE. - */ - protected $session_variables = FALSE; + /** + * @var bool Session variables that must be maintained across all connections, ie: SET TIME ZONE. + */ + protected $session_variables = false; - /** - * @var bool Called immediately after connecting to any DB. - */ - protected $user_defined_session_init_sql = FALSE; + /** + * @var bool Called immediately after connecting to any DB. + */ + protected $user_defined_session_init_sql = false; - /** - * Defines SQL queries that are executed each time a new database connection is established. - * - * @param $sql - * @return bool - */ - public function setSessionInitSQL( $sql ) - { - $this->user_defined_session_init_sql[] = $sql; - return TRUE; - } + /** + * Defines SQL queries that are executed each time a new database connection is established. + * + * @param $sql + * @return bool + */ + public function setSessionInitSQL($sql) + { + $this->user_defined_session_init_sql[] = $sql; - /** - * Adds a new database connection to the pool, but no actual connection is made until its needed. - * - * @param $obj - * @return bool - * @throws Exception - */ - public function addConnection( $obj ) - { - if ( $obj instanceof ADOdbLoadBalancerConnection ) { - $this->connections[] = $obj; - end( $this->connections ); - $i = key( $this->connections ); + return true; + } - $this->total_connections[$obj->type]++; - $this->total_connections['all']++; + /** + * Adds a new database connection to the pool, but no actual connection is made until its needed. + * + * @param $obj + * @return bool + * @throws Exception + */ + public function addConnection($obj) + { + if ($obj instanceof ADOdbLoadBalancerConnection) { + $this->connections[] = $obj; + end($this->connections); + $i = key($this->connections); - $this->total_connection_weights[$obj->type] += abs( $obj->weight ); - $this->total_connection_weights['all'] += abs( $obj->weight ); - - if ( $obj->type == 'master' ) { - $this->connections_master[] = $i; - } else { - $this->connections_slave[] = $i; - } - - return TRUE; - } + $this->total_connections[$obj->type]++; + $this->total_connections['all']++; - throw new Exception('Connection object is not an instance of ADOdbLoadBalancerConnection'); + $this->total_connection_weights[$obj->type] += abs($obj->weight); + $this->total_connection_weights['all'] += abs($obj->weight); - return FALSE; - } + if ($obj->type == 'master') { + $this->connections_master[] = $i; + } else { + $this->connections_slave[] = $i; + } - /** - * Removes a database connection from the pool. - * - * @param $i - * @return bool - */ - public function removeConnection( $i ) - { - $obj = $this->connections[$i]; + return true; + } - $this->total_connections[$obj->type]--; - $this->total_connections['all']--; + throw new Exception('Connection object is not an instance of ADOdbLoadBalancerConnection'); - $this->total_connection_weights[$obj->type] -= abs( $obj->weight ); - $this->total_connection_weights['all'] -= abs( $obj->weight ); + return false; + } - if ( $obj->type == 'master' ) { - unset($this->connections_master[array_search( $i, $this->connections_master )]); - $this->connections_master = array_values($this->connections_master); //Reindex array. - } else { - unset($this->connections_slave[array_search( $i, $this->connections_slave )]); - $this->connections_slave = array_values($this->connections_slave); //Reindex array. - } + /** + * Removes a database connection from the pool. + * + * @param $i + * @return bool + */ + public function removeConnection($i) + { + $obj = $this->connections[$i]; - //Remove any sticky connections as well. - if ( $this->last_connection_id[$obj->type] == $i ) { - $this->last_connection_id[$obj->type] = FALSE; - } + $this->total_connections[$obj->type]--; + $this->total_connections['all']--; - unset($this->connections[$i]); + $this->total_connection_weights[$obj->type] -= abs($obj->weight); + $this->total_connection_weights['all'] -= abs($obj->weight); - return TRUE; - } + if ($obj->type == 'master') { + unset($this->connections_master[array_search($i, $this->connections_master)]); + $this->connections_master = array_values($this->connections_master); //Reindex array. + } else { + unset($this->connections_slave[array_search($i, $this->connections_slave)]); + $this->connections_slave = array_values($this->connections_slave); //Reindex array. + } - /** - * Returns a database connection of the specified type, but takes into account the connection weight for load balancing. - * - * @param $type Type of database connection, either: 'master' or 'slave' - * @return bool|int|string - */ - private function getConnectionByWeight( $type ) - { - if ( $type == 'slave' ) { - $total_weight = $this->total_connection_weights['all']; - } else { - $total_weight = $this->total_connection_weights['master']; - } + //Remove any sticky connections as well. + if ($this->last_connection_id[$obj->type] == $i) { + $this->last_connection_id[$obj->type] = false; + } - $i = FALSE; - if ( is_array( $this->connections ) ) { - $n = 0; - $num = mt_rand(0, $total_weight ); - foreach( $this->connections as $i => $connection_obj ) { - if ( $connection_obj->weight > 0 && ( $type == 'slave' || $connection_obj->type == 'master' ) ) { - $n += $connection_obj->weight; - if ( $n >= $num) { - break; - } - } - } - } + unset($this->connections[$i]); - return $i; - } + return true; + } - /** - * Returns the proper database connection when taking into account sticky sessions and load balancing. - * - * @param $type - * @return bool|int|mixed|string - */ - public function getLoadBalancedConnection( $type ) - { - if ( $this->total_connections == 0 ) { - $connection_id = 0; - } else { - if ( $this->enable_sticky_sessions == TRUE && $this->last_connection_id[$type] !== FALSE ) { - $connection_id = $this->last_connection_id[$type]; - } else { - if ( $type == 'master' && $this->total_connections['master'] == 1 ) { - $connection_id = $this->connections_master[0]; - } else { - $connection_id = $this->getConnectionByWeight( $type ); - } - } - } + /** + * Returns a database connection of the specified type, but takes into account the connection weight for load balancing. + * + * @param $type Type of database connection, either: 'master' or 'slave' + * @return bool|int|string + */ + private function getConnectionByWeight($type) + { + if ($type == 'slave') { + $total_weight = $this->total_connection_weights['all']; + } else { + $total_weight = $this->total_connection_weights['master']; + } - return $connection_id; - } + $i = false; + if (is_array($this->connections)) { + $n = 0; + $num = mt_rand(0, $total_weight); + foreach ($this->connections as $i => $connection_obj) { + if ($connection_obj->weight > 0 && ($type == 'slave' || $connection_obj->type == 'master')) { + $n += $connection_obj->weight; + if ($n >= $num) { + break; + } + } + } + } - /** - * Returns the ADODB connection object by connection_id and ensures that its connected and the session variables are executed. - * - * @param $connection_id - * @return bool - * @throws Exception - */ - private function _getConnection( $connection_id ) - { - if ( isset($this->connections[$connection_id]) ) { - $connection_obj = $this->connections[$connection_id]; - $adodb_obj = $connection_obj->getADOdbObject(); - if ( is_object($adodb_obj) && $adodb_obj->_connectionID == FALSE ) { - try { - if ( $connection_obj->persistent_connection == TRUE ) { - $adodb_obj->Pconnect( $connection_obj->host, $connection_obj->user, $connection_obj->password, $connection_obj->database ); - } else { - $adodb_obj->Connect( $connection_obj->host, $connection_obj->user, $connection_obj->password, $connection_obj->database ); - } - } catch ( Exception $e ) { - //Connection error, see if there are other connections to try still. - throw $e; //No connections left, reThrow exception so application can catch it. - return FALSE; - } + return $i; + } - if ( is_array( $this->user_defined_session_init_sql ) ) { - foreach( $this->user_defined_session_init_sql as $session_init_sql ) { - $adodb_obj->Execute( $session_init_sql ); - } - } - $this->executeSessionVariables( $adodb_obj ); - } + /** + * Returns the proper database connection when taking into account sticky sessions and load balancing. + * + * @param $type + * @return bool|int|mixed|string + */ + public function getLoadBalancedConnection($type) + { + if ($this->total_connections == 0) { + $connection_id = 0; + } else { + if ($this->enable_sticky_sessions == true && $this->last_connection_id[$type] !== false) { + $connection_id = $this->last_connection_id[$type]; + } else { + if ($type == 'master' && $this->total_connections['master'] == 1) { + $connection_id = $this->connections_master[0]; + } else { + $connection_id = $this->getConnectionByWeight($type); + } + } + } - return $adodb_obj; - } else { - throw new Exception('Unable to return Connection object...'); - } - } + return $connection_id; + } - /** - * Returns the ADODB connection object by database type and ensures that its connected and the session variables are executed. - * - * @param string $type - * @param null $pin_connection - * @param bool $force_connection_id - * @return bool - * @throws Exception - */ - public function getConnection( $type = 'master', $pin_connection = NULL, $force_connection_id = FALSE ) - { - if ( $this->pinned_connection_id !== FALSE ) { - $connection_id = $this->pinned_connection_id; - } else { - $connection_id = $this->getLoadBalancedConnection( $type ); - } + /** + * Returns the ADODB connection object by connection_id and ensures that its connected and the session variables are executed. + * + * @param $connection_id + * @return bool + * @throws Exception + */ + private function _getConnection($connection_id) + { + if (isset($this->connections[$connection_id])) { + $connection_obj = $this->connections[$connection_id]; + $adodb_obj = $connection_obj->getADOdbObject(); + if (is_object($adodb_obj) && $adodb_obj->_connectionID == false) { + try { + if ($connection_obj->persistent_connection == true) { + $adodb_obj->Pconnect($connection_obj->host, $connection_obj->user, $connection_obj->password, + $connection_obj->database); + } else { + $adodb_obj->Connect($connection_obj->host, $connection_obj->user, $connection_obj->password, + $connection_obj->database); + } + } catch (Exception $e) { + //Connection error, see if there are other connections to try still. + throw $e; //No connections left, reThrow exception so application can catch it. + return false; + } - try { - $adodb_obj = $this->_getConnection( $connection_id ); - $connection_obj = $this->connections[$connection_id]; - } catch ( Exception $e ) { - //Connection error, see if there are other connections to try still. - if ( ($type == 'master' && $this->total_connections['master'] > 0 ) || ( $type == 'slave' && $this->total_connections['all'] > 0 ) ) { - $this->removeConnection( $connection_id ); - return $this->getConnection( $type, $pin_connection ); - } else { - throw $e; //No connections left, reThrow exception so application can catch it. - return FALSE; - } - } + if (is_array($this->user_defined_session_init_sql)) { + foreach ($this->user_defined_session_init_sql as $session_init_sql) { + $adodb_obj->Execute($session_init_sql); + } + } + $this->executeSessionVariables($adodb_obj); + } - $this->last_connection_id[$type] = $connection_id; + return $adodb_obj; + } else { + throw new Exception('Unable to return Connection object...'); + } + } - if ( $pin_connection === TRUE ) { - $this->pinned_connection_id = $connection_id; - } elseif( $pin_connection === FALSE && $adodb_obj->transOff <= 1 ) { //UnPin connection only if we are 1 level deep in a transaction. - $this->pinned_connection_id = FALSE; + /** + * Returns the ADODB connection object by database type and ensures that its connected and the session variables are executed. + * + * @param string $type + * @param null $pin_connection + * @param bool $force_connection_id + * @return bool + * @throws Exception + */ + public function getConnection($type = 'master', $pin_connection = null, $force_connection_id = false) + { + if ($this->pinned_connection_id !== false) { + $connection_id = $this->pinned_connection_id; + } else { + $connection_id = $this->getLoadBalancedConnection($type); + } - //When unpinning connection, reset last_connection_id so slave queries don't get stuck on the master. - $this->last_connection_id['master'] = FALSE; - $this->last_connection_id['slave'] = FALSE; - } + try { + $adodb_obj = $this->_getConnection($connection_id); + $connection_obj = $this->connections[$connection_id]; + } catch (Exception $e) { + //Connection error, see if there are other connections to try still. + if (($type == 'master' && $this->total_connections['master'] > 0) || ($type == 'slave' && $this->total_connections['all'] > 0)) { + $this->removeConnection($connection_id); - return $adodb_obj; - } + return $this->getConnection($type, $pin_connection); + } else { + throw $e; //No connections left, reThrow exception so application can catch it. + return false; + } + } - /** - * This is a hack to work around pass by reference error. - * Parameter 1 to ADOConnection::GetInsertSQL() expected to be a reference, value given in adodb-loadbalancer.inc.php on line 83 - * - * @param $arr - * @return array - */ - private function makeValuesReferenced( $arr ) - { - $refs = array(); + $this->last_connection_id[$type] = $connection_id; - foreach( $arr as $key => $value ) { - $refs[$key] = &$arr[$key]; - } + if ($pin_connection === true) { + $this->pinned_connection_id = $connection_id; + } elseif ($pin_connection === false && $adodb_obj->transOff <= 1) { //UnPin connection only if we are 1 level deep in a transaction. + $this->pinned_connection_id = false; - return $refs; - } - - /** - * Allow setting session variables that are maintained across connections. - * - * Its important that these are set using name/value, so it can determine if the same variable is set multiple times - * causing bloat/clutter when new connections are established. For example if the time_zone is set to many different - * ones through the course of a single connection, a new connection should only set it to the most recent value. - * - * - * @param $name - * @param $value - * @param bool $execute_immediately - * @return array|bool|mixed - */ - public function setSessionVariable( $name, $value, $execute_immediately = TRUE ) - { - $this->session_variables[$name] = $value; + //When unpinning connection, reset last_connection_id so slave queries don't get stuck on the master. + $this->last_connection_id['master'] = false; + $this->last_connection_id['slave'] = false; + } - if ( $execute_immediately == TRUE ) { - return $this->executeSessionVariables(); - } else { - return TRUE; - } - } + return $adodb_obj; + } - /** - * Executes the session variables on a given ADODB object. - * - * @param bool $adodb_obj - * @return array|bool|mixed - */ - private function executeSessionVariables( $adodb_obj = FALSE ) - { - if ( is_array( $this->session_variables ) ) { - $sql = ''; - foreach( $this->session_variables as $name => $value ) { - //$sql .= 'SET SESSION '. $name .' '. $value; - //MySQL uses: SET SESSION foo_bar='foo' - //PGSQL uses: SET SESSION foo_bar 'foo' - //So leave it up to the user to pass the proper value with '=' if needed. - //This may be a candidate to move into ADOdb proper. - $sql .= 'SET SESSION '. $name .' '. $value; - } + /** + * This is a hack to work around pass by reference error. + * Parameter 1 to ADOConnection::GetInsertSQL() expected to be a reference, value given in adodb-loadbalancer.inc.php on line 83 + * + * @param $arr + * @return array + */ + private function makeValuesReferenced($arr) + { + $refs = array(); - if ( $adodb_obj !== FALSE ) { - return $adodb_obj->Execute( $sql ); - } else { - return $this->ClusterExecute( $sql ); - } - } + foreach ($arr as $key => $value) { + $refs[$key] = &$arr[$key]; + } - return FALSE; - } + return $refs; + } - /** - * Executes the same SQL QUERY on the entire cluster of connections. - * Would be used for things like SET SESSION TIME ZONE calls and such. - * - * @param $sql - * @param bool $inputarr - * @param bool $return_all_results - * @param bool $existing_connections_only - * @return array|bool|mixed - * @throws Exception - */ - public function ClusterExecute( $sql, $inputarr = FALSE, $return_all_results = FALSE, $existing_connections_only = TRUE ) - { - if ( is_array($this->connections) && count($this->connections) > 0 ) { - foreach( $this->connections as $key => $connection_obj ) { - if ( $existing_connections_only == FALSE || ( $existing_connections_only == TRUE && $connection_obj->getADOdbObject()->_connectionID !== FALSE ) ) { - $adodb_obj = $this->_getConnection( $key ); - if ( is_object( $adodb_obj ) ) { - $result_arr[] = $adodb_obj->Execute( $sql, $inputarr ); - } - } - } + /** + * Allow setting session variables that are maintained across connections. + * + * Its important that these are set using name/value, so it can determine if the same variable is set multiple times + * causing bloat/clutter when new connections are established. For example if the time_zone is set to many different + * ones through the course of a single connection, a new connection should only set it to the most recent value. + * + * + * @param $name + * @param $value + * @param bool $execute_immediately + * @return array|bool|mixed + */ + public function setSessionVariable($name, $value, $execute_immediately = true) + { + $this->session_variables[$name] = $value; - if ( isset($result_arr) && $return_all_results == TRUE ) { - return $result_arr; - } else { - //Loop through all results checking to see if they match, if they do return the first one - //otherwise return an array of all results. - if ( isset($result_arr) ) { - foreach( $result_arr as $result ) { - if ( $result == FALSE ) { - return $result_arr; - } - } + if ($execute_immediately == true) { + return $this->executeSessionVariables(); + } else { + return true; + } + } - return $result_arr[0]; - } - } - } + /** + * Executes the session variables on a given ADODB object. + * + * @param bool $adodb_obj + * @return array|bool|mixed + */ + private function executeSessionVariables($adodb_obj = false) + { + if (is_array($this->session_variables)) { + $sql = ''; + foreach ($this->session_variables as $name => $value) { + //$sql .= 'SET SESSION '. $name .' '. $value; + //MySQL uses: SET SESSION foo_bar='foo' + //PGSQL uses: SET SESSION foo_bar 'foo' + //So leave it up to the user to pass the proper value with '=' if needed. + //This may be a candidate to move into ADOdb proper. + $sql .= 'SET SESSION ' . $name . ' ' . $value; + } - return FALSE; - } + if ($adodb_obj !== false) { + return $adodb_obj->Execute($sql); + } else { + return $this->ClusterExecute($sql); + } + } - /** - * Determines if a SQL query is read-only or not. - * - * @param $sql SQL Query to test. - * @return bool - */ - public function isReadOnlyQuery( $sql ) - { - if ( stripos( $sql, 'SELECT') === 0 && stripos( $sql, 'FOR UPDATE') === FALSE && stripos( $sql, ' INTO ') === FALSE && stripos( $sql, 'LOCK IN') === FALSE ) { - return TRUE; - } + return false; + } - return FALSE; - } - - /** - * Use this instead of __call() as it significantly reduces the overhead of call_user_func_array(). - * - * @param $sql - * @param bool $inputarr - * @return array|bool|mixed - * @throws Exception - */ - public function Execute( $sql, $inputarr = FALSE ) - { - $type = 'master'; - $pin_connection = NULL; + /** + * Executes the same SQL QUERY on the entire cluster of connections. + * Would be used for things like SET SESSION TIME ZONE calls and such. + * + * @param $sql + * @param bool $inputarr + * @param bool $return_all_results + * @param bool $existing_connections_only + * @return array|bool|mixed + * @throws Exception + */ + public function ClusterExecute( + $sql, + $inputarr = false, + $return_all_results = false, + $existing_connections_only = true + ) { + if (is_array($this->connections) && count($this->connections) > 0) { + foreach ($this->connections as $key => $connection_obj) { + if ($existing_connections_only == false || ($existing_connections_only == true && $connection_obj->getADOdbObject()->_connectionID !== false)) { + $adodb_obj = $this->_getConnection($key); + if (is_object($adodb_obj)) { + $result_arr[] = $adodb_obj->Execute($sql, $inputarr); + } + } + } - //SELECT queries that can write and therefore must be run on MASTER. - //SELECT ... FOR UPDATE; - //SELECT ... INTO ... - //SELECT .. LOCK IN ... (MYSQL) - if ( $this->isReadOnlyQuery( $sql ) == TRUE ) { - $type = 'slave'; - } elseif ( stripos( $sql, 'SET') === 0 ) { - //SET SQL statements should likely use setSessionVariable() instead, - //so state is properly maintained across connections, especially when they are lazily created. - return $this->ClusterExecute( $sql, $inputarr ); - } + if (isset($result_arr) && $return_all_results == true) { + return $result_arr; + } else { + //Loop through all results checking to see if they match, if they do return the first one + //otherwise return an array of all results. + if (isset($result_arr)) { + foreach ($result_arr as $result) { + if ($result == false) { + return $result_arr; + } + } - $adodb_obj = $this->getConnection( $type, $pin_connection ); - if ( $adodb_obj !== FALSE ) { - return $adodb_obj->Execute( $sql, $inputarr ); - } + return $result_arr[0]; + } + } + } - return FALSE; - } + return false; + } - /** - * Magic method to intercept method calls back to the proper ADODB object for master/slaves. - * - * @param $method ADODB method to call. - * @param $args Arguments to the ADODB method. - * @return bool|mixed - * @throws Exception - */ - public function __call( $method, $args ) - { - $type = 'master'; - $pin_connection = NULL; + /** + * Determines if a SQL query is read-only or not. + * + * @param $sql SQL Query to test. + * @return bool + */ + public function isReadOnlyQuery($sql) + { + if (stripos($sql, 'SELECT') === 0 && stripos($sql, 'FOR UPDATE') === false && stripos($sql, + ' INTO ') === false && stripos($sql, 'LOCK IN') === false + ) { + return true; + } - //Intercept specific methods to determine if they are read-only or not. - $method = strtolower($method); - switch ( $method ) { - //case 'execute': //This is the direct overloaded function above instead. - case 'getone': - case 'getrow': - case 'getall': - case 'getcol': - case 'getassoc': - case 'selectlimit': - if ( $this->isReadOnlyQuery( $args[0] ) == TRUE ) { - $type = 'slave'; - } - break; - case 'cachegetone': - case 'cachegetrow': - case 'cachegetall': - case 'cachegetcol': - case 'cachegetassoc': - case 'cacheexecute': - case 'cacheselect': - case 'pageexecute': - case 'cachepageexecute': - $type = 'slave'; - break; - //case 'ignoreerrors': - // //When ignoreerrors is called, PIN to the connection until its called again. - // if ( !isset($args[0]) || ( isset($args[0]) && $args[0] == FALSE ) ) { - // $pin_connection = TRUE; - // } else { - // $pin_connection = FALSE; - // } - // break; + return false; + } - //Manual transactions - case 'begintrans': - $pin_connection = TRUE; - break; - case 'rollbacktrans': - case 'committrans': - $pin_connection = FALSE; - break; - //Smart transactions - case 'starttrans': - $pin_connection = TRUE; - break; - case 'completetrans': - case 'failtrans': - //getConnection() will only unpin the transaction if we're exiting the last nested transaction - $pin_connection = FALSE; - break; - default: - break; - } + /** + * Use this instead of __call() as it significantly reduces the overhead of call_user_func_array(). + * + * @param $sql + * @param bool $inputarr + * @return array|bool|mixed + * @throws Exception + */ + public function Execute($sql, $inputarr = false) + { + $type = 'master'; + $pin_connection = null; - $adodb_obj = $this->getConnection( $type, $pin_connection ); - if ( is_object( $adodb_obj ) ) { - $result = call_user_func_array( array( $adodb_obj, $method ), $this->makeValuesReferenced( $args ) ); - return $result; - } + //SELECT queries that can write and therefore must be run on MASTER. + //SELECT ... FOR UPDATE; + //SELECT ... INTO ... + //SELECT .. LOCK IN ... (MYSQL) + if ($this->isReadOnlyQuery($sql) == true) { + $type = 'slave'; + } elseif (stripos($sql, 'SET') === 0) { + //SET SQL statements should likely use setSessionVariable() instead, + //so state is properly maintained across connections, especially when they are lazily created. + return $this->ClusterExecute($sql, $inputarr); + } - return FALSE; - } + $adodb_obj = $this->getConnection($type, $pin_connection); + if ($adodb_obj !== false) { + return $adodb_obj->Execute($sql, $inputarr); + } - /** - * Magic method to proxy property getter calls back to the proper ADODB object currently in use. - * - * @param $property - * @return mixed - * @throws Exception - */ - public function __get( $property ) - { - return $this->getConnection()->$property; - } + return false; + } - /** - * Magic method to proxy property setter calls back to the proper ADODB object currently in use. - * - * @param $property - * @param $value - * @return mixed - * @throws Exception - */ - public function __set( $property, $value ) - { - return $this->getConnection()->$property = $value; - } + /** + * Magic method to intercept method calls back to the proper ADODB object for master/slaves. + * + * @param $method ADODB method to call. + * @param $args Arguments to the ADODB method. + * @return bool|mixed + * @throws Exception + */ + public function __call($method, $args) + { + $type = 'master'; + $pin_connection = null; - /** - * Override the __clone() magic method. - */ - private function __clone() { } + //Intercept specific methods to determine if they are read-only or not. + $method = strtolower($method); + switch ($method) { + //case 'execute': //This is the direct overloaded function above instead. + case 'getone': + case 'getrow': + case 'getall': + case 'getcol': + case 'getassoc': + case 'selectlimit': + if ($this->isReadOnlyQuery($args[0]) == true) { + $type = 'slave'; + } + break; + case 'cachegetone': + case 'cachegetrow': + case 'cachegetall': + case 'cachegetcol': + case 'cachegetassoc': + case 'cacheexecute': + case 'cacheselect': + case 'pageexecute': + case 'cachepageexecute': + $type = 'slave'; + break; + //case 'ignoreerrors': + // //When ignoreerrors is called, PIN to the connection until its called again. + // if ( !isset($args[0]) || ( isset($args[0]) && $args[0] == FALSE ) ) { + // $pin_connection = TRUE; + // } else { + // $pin_connection = FALSE; + // } + // break; + + //Manual transactions + case 'begintrans': + $pin_connection = true; + break; + case 'rollbacktrans': + case 'committrans': + $pin_connection = false; + break; + //Smart transactions + case 'starttrans': + $pin_connection = true; + break; + case 'completetrans': + case 'failtrans': + //getConnection() will only unpin the transaction if we're exiting the last nested transaction + $pin_connection = false; + break; + default: + break; + } + + $adodb_obj = $this->getConnection($type, $pin_connection); + if (is_object($adodb_obj)) { + $result = call_user_func_array(array($adodb_obj, $method), $this->makeValuesReferenced($args)); + + return $result; + } + + return false; + } + + /** + * Magic method to proxy property getter calls back to the proper ADODB object currently in use. + * + * @param $property + * @return mixed + * @throws Exception + */ + public function __get($property) + { + return $this->getConnection()->$property; + } + + /** + * Magic method to proxy property setter calls back to the proper ADODB object currently in use. + * + * @param $property + * @param $value + * @return mixed + * @throws Exception + */ + public function __set($property, $value) + { + return $this->getConnection()->$property = $value; + } + + /** + * Override the __clone() magic method. + */ + private function __clone() + { + } } /** * Class ADOdbLoadBalancerConnection */ -class ADOdbLoadBalancerConnection { - /** - * @var bool ADOdb drive name. - */ - protected $driver = FALSE; +class ADOdbLoadBalancerConnection +{ + /** + * @var bool ADOdb drive name. + */ + protected $driver = false; + + /** + * @var bool ADODB object. + */ + protected $adodb_obj = false; - /** - * @var bool ADODB object. - */ - protected $adodb_obj = FALSE; + /** + * @var string Type of connection, either 'master' or 'slave' + */ + public $type = 'master'; - /** - * @var string Type of connection, either 'master' or 'slave' - */ - public $type = 'master'; + /** + * @var int Weight of connection, lower receives less queries, higher receives more queries. + */ + public $weight = 1; - /** - * @var int Weight of connection, lower receives less queries, higher receives more queries. - */ - public $weight = 1; + /** + * @var bool Determines if the connection persistent. + */ + public $persistent_connection = false; - /** - * @var bool Determines if the connection persistent. - */ - public $persistent_connection = FALSE; + /** + * @var string Database connection host + */ + public $host = ''; - /** - * @var string Database connection host - */ - public $host = ''; + /** + * @var string Database connection user + */ + public $user = ''; - /** - * @var string Database connection user - */ - public $user = ''; + /** + * @var string Database connection password + */ + public $password = ''; - /** - * @var string Database connection password - */ - public $password = ''; + /** + * @var string Database connection database name + */ + public $database = ''; - /** - * @var string Database connection database name - */ - public $database = ''; + /** + * ADOdbLoadBalancerConnection constructor to setup the ADODB object. + * + * @param $driver + * @param string $type + * @param int $weight + * @param bool $persistent_connection + * @param string $argHostname + * @param string $argUsername + * @param string $argPassword + * @param string $argDatabaseName + */ + public function __construct( + $driver, + $type = 'master', + $weight = 1, + $persistent_connection = false, + $argHostname = '', + $argUsername = '', + $argPassword = '', + $argDatabaseName = '' + ) { + if ($type !== 'master' && $type !== 'slave') { + return false; + } - /** - * ADOdbLoadBalancerConnection constructor to setup the ADODB object. - * - * @param $driver - * @param string $type - * @param int $weight - * @param bool $persistent_connection - * @param string $argHostname - * @param string $argUsername - * @param string $argPassword - * @param string $argDatabaseName - */ - public function __construct( $driver, $type = 'master', $weight = 1, $persistent_connection = FALSE, $argHostname = '', $argUsername = '', $argPassword = '', $argDatabaseName = '' ) - { - if ( $type !== 'master' && $type !== 'slave' ) { - return FALSE; - } + $this->adodb_obj = ADONewConnection($driver); - $this->adodb_obj = ADONewConnection( $driver ); + $this->type = $type; + $this->weight = $weight; + $this->persistent_connection = $persistent_connection; - $this->type = $type; - $this->weight = $weight; - $this->persistent_connection = $persistent_connection; - - $this->host = $argHostname; - $this->user = $argUsername; - $this->password = $argPassword; - $this->database = $argDatabaseName; + $this->host = $argHostname; + $this->user = $argUsername; + $this->password = $argPassword; + $this->database = $argDatabaseName; - return TRUE; - } + return true; + } - /** - * Returns the ADODB object for this connection. - * - * @return bool - */ - public function getADOdbObject() - { - return $this->adodb_obj; - } + /** + * Returns the ADODB object for this connection. + * + * @return bool + */ + public function getADOdbObject() + { + return $this->adodb_obj; + } } |
