Skip to content

Commit

Permalink
Merge pull request #3353 from tractorcow/pulls/3.2/fix-pdo
Browse files Browse the repository at this point in the history
BUG Fix PDOConnector issues
  • Loading branch information
chillu committed Aug 1, 2014
2 parents bab4a24 + b0239f4 commit fc1eb1c
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 65 deletions.
10 changes: 5 additions & 5 deletions .travis.yml
Expand Up @@ -14,20 +14,20 @@ env:

matrix:
include:
- php: 5.3
env: DB=PGSQL CORE_RELEASE=master
- php: 5.3
- php: 5.4
env: DB=SQLITE CORE_RELEASE=master
- php: 5.4
env: DB=PGSQL CORE_RELEASE=master
- php: 5.4
env: DB=MYSQL CORE_RELEASE=master
- php: 5.4
env: DB=MYSQL CORE_RELEASE=master PDO=1
- php: 5.5
env: DB=MYSQL CORE_RELEASE=master
- php: 5.6
env: DB=MYSQL CORE_RELEASE=master
- php: 5.4
env: DB=MYSQL CORE_RELEASE=master BEHAT_TEST=1
- php: 5.6
env: DB=MYSQL CORE_RELEASE=master

before_script:
- composer self-update
Expand Down
33 changes: 16 additions & 17 deletions model/connect/MySQLDatabase.php
Expand Up @@ -9,10 +9,10 @@
* @subpackage model
*/
class MySQLDatabase extends SS_Database {

/**
* Default connection charset (may be overridden in $databaseConfig)
*
*
* @config
* @var String
*/
Expand All @@ -23,9 +23,9 @@ public function connect($parameters) {
if(empty($parameters['driver'])) {
$parameters['driver'] = $this->getDatabaseServer();
}

// Set charset
if( empty($parameters['charset'])
if( empty($parameters['charset'])
&& ($charset = Config::inst()->get('MySQLDatabase', 'connection_charset'))
) {
$parameters['charset'] = $charset;
Expand All @@ -42,13 +42,13 @@ public function connect($parameters) {
}

// SS_Database subclass maintains responsibility for selecting database
// once connected in order to correctly handle schema queries about
// once connected in order to correctly handle schema queries about
// existence of database, error handling at the correct level, etc
if (!empty($parameters['database'])) {
$this->selectDatabase($parameters['database'], false, false);
}
}

/**
* Sets the character set for the MySQL database connection.
*
Expand All @@ -68,7 +68,7 @@ public static function set_connection_charset($charset = 'utf8') {

/**
* Sets the SQL mode
*
*
* @param string $mode Connection mode
*/
public function setSQLMode($mode) {
Expand All @@ -78,7 +78,7 @@ public function setSQLMode($mode) {

/**
* Sets the system timezone for the database connection
*
*
* @param string $timezone
*/
public function selectTimezone($timezone) {
Expand Down Expand Up @@ -112,7 +112,6 @@ public function searchEngine($classesToSearch, $keywords, $start, $pageLength, $
if (!class_exists('File'))
throw new Exception('MySQLDatabase->searchEngine() requires "File" class');

$fileFilter = '';
$keywords = $this->escapeString($keywords);
$htmlEntityKeywords = htmlentities($keywords, ENT_NOQUOTES, 'UTF-8');

Expand All @@ -131,7 +130,7 @@ public function searchEngine($classesToSearch, $keywords, $start, $pageLength, $
// Always ensure that only pages with ShowInSearch = 1 can be searched
$extraFilters['SiteTree'] .= " AND ShowInSearch <> 0";

// File.ShowInSearch was added later, keep the database driver backwards compatible
// File.ShowInSearch was added later, keep the database driver backwards compatible
// by checking for its existence first
$fields = $this->fieldList('File');
if (array_key_exists('ShowInSearch', $fields))
Expand Down Expand Up @@ -201,7 +200,7 @@ public function searchEngine($classesToSearch, $keywords, $start, $pageLength, $

$querySQLs[] = $query->sql($parameters);
$queryParameters = array_merge($queryParameters, $parameters);

$totalCount += $query->unlimitedRowCount();
}
$fullQuery = implode(" UNION ", $querySQLs) . " ORDER BY $sortBy LIMIT $limit";
Expand Down Expand Up @@ -233,30 +232,30 @@ public function supportsTransactions() {
public function transactionStart($transactionMode = false, $sessionCharacteristics = false) {
// This sets the isolation level for the NEXT transaction, not the current one.
if ($transactionMode) {
$this->query('SET TRANSACTION ' . $transactionMode . ';');
$this->query('SET TRANSACTION ' . $transactionMode);
}

$this->query('START TRANSACTION;');
$this->query('START TRANSACTION');

if ($sessionCharacteristics) {
$this->query('SET SESSION TRANSACTION ' . $sessionCharacteristics . ';');
$this->query('SET SESSION TRANSACTION ' . $sessionCharacteristics);
}
}

public function transactionSavepoint($savepoint) {
$this->query("SAVEPOINT $savepoint;");
$this->query("SAVEPOINT $savepoint");
}

public function transactionRollback($savepoint = false) {
if ($savepoint) {
$this->query('ROLLBACK TO ' . $savepoint . ';');
$this->query('ROLLBACK TO ' . $savepoint);
} else {
$this->query('ROLLBACK');
}
}

public function transactionEnd($chain = false) {
$this->query('COMMIT AND ' . ($chain ? '' : 'NO ') . 'CHAIN;');
$this->query('COMMIT AND ' . ($chain ? '' : 'NO ') . 'CHAIN');
}

public function comparisonClause($field, $value, $exact = false, $negate = false, $caseSensitive = null,
Expand Down
80 changes: 41 additions & 39 deletions model/connect/PDOConnector.php
Expand Up @@ -6,54 +6,54 @@
* @subpackage model
*/
class PDOConnector extends DBConnector {

/**
* Should ATTR_EMULATE_PREPARES flag be used to emulate prepared statements?
*
*
* @config
* @var boolean
*/
private static $emulate_prepare = false;

/**
* The PDO connection instance
*
* @var PDO
*
* @var PDO
*/
protected $pdoConnection = null;

/**
* Name of the currently selected database
*
*
* @var string
*/
protected $databaseName = null;

/**
* The most recent statement returned from PDODatabase->query
*
*
* @var PDOStatement
*/
protected $lastStatement = null;

/**
* List of prepared statements, cached by SQL string
*
* @var array
*/
protected $cachedStatements = array();

/**
* Flush all prepared statements
*/
public function flushStatements() {
$this->cachedStatements = array();
}

/**
* Retrieve a prepared statement for a given SQL string, or return an already prepared version if
* one exists for the given query
*
*
* @param string $sql
* @return PDOStatement
*/
Expand All @@ -66,10 +66,10 @@ public function getOrPrepareStatement($sql) {
}
return $this->cachedStatements[$sql];
}

/**
* Is PDO running in emulated mode
*
*
* @return boolean
*/
public static function is_emulate_prepare() {
Expand All @@ -84,22 +84,22 @@ public function connect($parameters, $selectDB = false) {
// requested via selectDatabase
$driver = $parameters['driver'] . ":";
$dsn = array();

// Typically this is false, but some drivers will request this
if($selectDB) {
// Specify complete file path immediately following driver (SQLLite3)
if(!empty($parameters['filepath'])) {
$dsn[] = $parameters['filepath'];
} elseif(!empty($parameters['database'])) {
// Some databases require a selected database at connection (SQLite3, Azure)
if($parameters['driver'] === 'sqlsrv') {
if($parameters['driver'] === 'sqlsrv') {
$dsn[] = "Database={$parameters['database']}";
} else {
$dsn[] = "dbname={$parameters['database']}";
}
}
}

// Syntax for sql server is slightly different
if($parameters['driver'] === 'sqlsrv') {
$server = $parameters['server'];
Expand Down Expand Up @@ -128,18 +128,20 @@ public function connect($parameters, $selectDB = false) {

// Connection commands to be run on every re-connection
$options = array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
PDO::ATTR_EMULATE_PREPARES => self::is_emulate_prepare()
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'
);
if(self::is_emulate_prepare()) {
$options[PDO::ATTR_EMULATE_PREPARES] = true;
}

// May throw a PDOException if fails
if(empty($parameters['username']) || empty($parameters['password'])) {
$this->pdoConnection = new PDO($driver.implode(';', $dsn));
} else {
$this->pdoConnection = new PDO($driver.implode(';', $dsn), $parameters['username'],
$parameters['password'], $options);
}
$this->pdoConnection = new PDO(
$driver.implode(';', $dsn),
empty($parameters['username']) ? '' : $parameters['username'],
empty($parameters['password']) ? '' : $parameters['password'],
$options
);

// Show selected DB if requested
if($this->pdoConnection && $selectDB && !empty($parameters['database'])) {
$this->databaseName = $parameters['database'];
Expand All @@ -164,10 +166,10 @@ public function escapeString($value) {
public function quoteString($value) {
return $this->pdoConnection->quote($value);
}

/**
* Executes a query that doesn't return a resultset
*
*
* @param string $sql
* @param string $sql The SQL query to execute
* @param integer $errorLevel For errors to this query, raise PHP errors
Expand All @@ -176,7 +178,7 @@ public function quoteString($value) {
public function exec($sql, $errorLevel = E_USER_ERROR) {
// Check if we should only preview this query
if ($this->previewWrite($sql)) return;

// Reset last statement to prevent interference in case of error
$this->lastStatement = null;

Expand Down Expand Up @@ -213,7 +215,7 @@ public function query($sql, $errorLevel = E_USER_ERROR) {

return new PDOQuery($this->lastStatement);
}

/**
* Determines the PDO::PARAM_* type for a given PHP type string
* @param string $phpType Type of object in PHP
Expand Down Expand Up @@ -241,10 +243,10 @@ public function getPDOParamType($phpType) {
user_error("Cannot bind parameter as it is an unsupported type ($phpType)", E_USER_ERROR);
}
}

/**
* Bind all parameters to a PDOStatement
*
*
* @param PDOStatement $statement
* @param array $parameters
*/
Expand All @@ -259,7 +261,7 @@ public function bindParameters(PDOStatement $statement, $parameters) {
$phpType = $value['type'];
$value = $value['value'];
}

// Check type of parameter
$type = $this->getPDOParamType($phpType);
if($type === PDO::PARAM_STR) $value = strval($value);
Expand All @@ -268,22 +270,22 @@ public function bindParameters(PDOStatement $statement, $parameters) {
$statement->bindValue($index+1, $value, $type);
}
}

public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR) {
// Check if we should only preview this query
if ($this->previewWrite($sql)) return;

// Benchmark query
$self = $this;
$this->lastStatement = $this->benchmarkQuery($sql, function($sql) use($parameters, $self) {

// Prepare statement
$statement = $self->getOrPrepareStatement($sql);
if(!$statement) return null;

// Inject parameters
$self->bindParameters($statement, $parameters);

// Safely execute the statement
$statement->execute($parameters);
return $statement;
Expand All @@ -298,17 +300,17 @@ public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR) {

return new PDOQuery($this->lastStatement);
}

/**
* Determine if a resource has an attached error
*
*
* @param PDOStatement|PDO $resource the resource to check
* @return boolean Flag indicating true if the resource has an error
*/
protected function hasError($resource) {
// No error if no resource
if(empty($resource)) return false;

// If the error code is empty the statement / connection has not been run yet
$code = $resource->errorCode();
if(empty($code)) return false;
Expand Down
4 changes: 0 additions & 4 deletions model/connect/PDOQuery.php
Expand Up @@ -27,10 +27,6 @@ public function __construct(PDOStatement $statement) {
$statement->closeCursor();
}

public function __destruct() {
$this->statement->closeCursor();
}

public function seek($row) {
$this->rowNum = $row - 1;
return $this->nextRecord();
Expand Down

0 comments on commit fc1eb1c

Please sign in to comment.