mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge pull request #3353 from tractorcow/pulls/3.2/fix-pdo
BUG Fix PDOConnector issues
This commit is contained in:
commit
fc1eb1cfbc
10
.travis.yml
10
.travis.yml
@ -14,20 +14,20 @@ env:
|
|||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- php: 5.3
|
- php: 5.4
|
||||||
env: DB=PGSQL CORE_RELEASE=master
|
|
||||||
- php: 5.3
|
|
||||||
env: DB=SQLITE CORE_RELEASE=master
|
env: DB=SQLITE CORE_RELEASE=master
|
||||||
|
- php: 5.4
|
||||||
|
env: DB=PGSQL CORE_RELEASE=master
|
||||||
- php: 5.4
|
- php: 5.4
|
||||||
env: DB=MYSQL CORE_RELEASE=master
|
env: DB=MYSQL CORE_RELEASE=master
|
||||||
|
- php: 5.4
|
||||||
|
env: DB=MYSQL CORE_RELEASE=master PDO=1
|
||||||
- php: 5.5
|
- php: 5.5
|
||||||
env: DB=MYSQL CORE_RELEASE=master
|
env: DB=MYSQL CORE_RELEASE=master
|
||||||
- php: 5.6
|
- php: 5.6
|
||||||
env: DB=MYSQL CORE_RELEASE=master
|
env: DB=MYSQL CORE_RELEASE=master
|
||||||
- php: 5.4
|
- php: 5.4
|
||||||
env: DB=MYSQL CORE_RELEASE=master BEHAT_TEST=1
|
env: DB=MYSQL CORE_RELEASE=master BEHAT_TEST=1
|
||||||
- php: 5.6
|
|
||||||
env: DB=MYSQL CORE_RELEASE=master
|
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- composer self-update
|
- composer self-update
|
||||||
|
@ -9,10 +9,10 @@
|
|||||||
* @subpackage model
|
* @subpackage model
|
||||||
*/
|
*/
|
||||||
class MySQLDatabase extends SS_Database {
|
class MySQLDatabase extends SS_Database {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default connection charset (may be overridden in $databaseConfig)
|
* Default connection charset (may be overridden in $databaseConfig)
|
||||||
*
|
*
|
||||||
* @config
|
* @config
|
||||||
* @var String
|
* @var String
|
||||||
*/
|
*/
|
||||||
@ -23,9 +23,9 @@ class MySQLDatabase extends SS_Database {
|
|||||||
if(empty($parameters['driver'])) {
|
if(empty($parameters['driver'])) {
|
||||||
$parameters['driver'] = $this->getDatabaseServer();
|
$parameters['driver'] = $this->getDatabaseServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set charset
|
// Set charset
|
||||||
if( empty($parameters['charset'])
|
if( empty($parameters['charset'])
|
||||||
&& ($charset = Config::inst()->get('MySQLDatabase', 'connection_charset'))
|
&& ($charset = Config::inst()->get('MySQLDatabase', 'connection_charset'))
|
||||||
) {
|
) {
|
||||||
$parameters['charset'] = $charset;
|
$parameters['charset'] = $charset;
|
||||||
@ -42,13 +42,13 @@ class MySQLDatabase extends SS_Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SS_Database subclass maintains responsibility for selecting database
|
// 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
|
// existence of database, error handling at the correct level, etc
|
||||||
if (!empty($parameters['database'])) {
|
if (!empty($parameters['database'])) {
|
||||||
$this->selectDatabase($parameters['database'], false, false);
|
$this->selectDatabase($parameters['database'], false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the character set for the MySQL database connection.
|
* Sets the character set for the MySQL database connection.
|
||||||
*
|
*
|
||||||
@ -68,7 +68,7 @@ class MySQLDatabase extends SS_Database {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the SQL mode
|
* Sets the SQL mode
|
||||||
*
|
*
|
||||||
* @param string $mode Connection mode
|
* @param string $mode Connection mode
|
||||||
*/
|
*/
|
||||||
public function setSQLMode($mode) {
|
public function setSQLMode($mode) {
|
||||||
@ -78,7 +78,7 @@ class MySQLDatabase extends SS_Database {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the system timezone for the database connection
|
* Sets the system timezone for the database connection
|
||||||
*
|
*
|
||||||
* @param string $timezone
|
* @param string $timezone
|
||||||
*/
|
*/
|
||||||
public function selectTimezone($timezone) {
|
public function selectTimezone($timezone) {
|
||||||
@ -112,7 +112,6 @@ class MySQLDatabase extends SS_Database {
|
|||||||
if (!class_exists('File'))
|
if (!class_exists('File'))
|
||||||
throw new Exception('MySQLDatabase->searchEngine() requires "File" class');
|
throw new Exception('MySQLDatabase->searchEngine() requires "File" class');
|
||||||
|
|
||||||
$fileFilter = '';
|
|
||||||
$keywords = $this->escapeString($keywords);
|
$keywords = $this->escapeString($keywords);
|
||||||
$htmlEntityKeywords = htmlentities($keywords, ENT_NOQUOTES, 'UTF-8');
|
$htmlEntityKeywords = htmlentities($keywords, ENT_NOQUOTES, 'UTF-8');
|
||||||
|
|
||||||
@ -131,7 +130,7 @@ class MySQLDatabase extends SS_Database {
|
|||||||
// Always ensure that only pages with ShowInSearch = 1 can be searched
|
// Always ensure that only pages with ShowInSearch = 1 can be searched
|
||||||
$extraFilters['SiteTree'] .= " AND ShowInSearch <> 0";
|
$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
|
// by checking for its existence first
|
||||||
$fields = $this->fieldList('File');
|
$fields = $this->fieldList('File');
|
||||||
if (array_key_exists('ShowInSearch', $fields))
|
if (array_key_exists('ShowInSearch', $fields))
|
||||||
@ -201,7 +200,7 @@ class MySQLDatabase extends SS_Database {
|
|||||||
|
|
||||||
$querySQLs[] = $query->sql($parameters);
|
$querySQLs[] = $query->sql($parameters);
|
||||||
$queryParameters = array_merge($queryParameters, $parameters);
|
$queryParameters = array_merge($queryParameters, $parameters);
|
||||||
|
|
||||||
$totalCount += $query->unlimitedRowCount();
|
$totalCount += $query->unlimitedRowCount();
|
||||||
}
|
}
|
||||||
$fullQuery = implode(" UNION ", $querySQLs) . " ORDER BY $sortBy LIMIT $limit";
|
$fullQuery = implode(" UNION ", $querySQLs) . " ORDER BY $sortBy LIMIT $limit";
|
||||||
@ -233,30 +232,30 @@ class MySQLDatabase extends SS_Database {
|
|||||||
public function transactionStart($transactionMode = false, $sessionCharacteristics = false) {
|
public function transactionStart($transactionMode = false, $sessionCharacteristics = false) {
|
||||||
// This sets the isolation level for the NEXT transaction, not the current one.
|
// This sets the isolation level for the NEXT transaction, not the current one.
|
||||||
if ($transactionMode) {
|
if ($transactionMode) {
|
||||||
$this->query('SET TRANSACTION ' . $transactionMode . ';');
|
$this->query('SET TRANSACTION ' . $transactionMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->query('START TRANSACTION;');
|
$this->query('START TRANSACTION');
|
||||||
|
|
||||||
if ($sessionCharacteristics) {
|
if ($sessionCharacteristics) {
|
||||||
$this->query('SET SESSION TRANSACTION ' . $sessionCharacteristics . ';');
|
$this->query('SET SESSION TRANSACTION ' . $sessionCharacteristics);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function transactionSavepoint($savepoint) {
|
public function transactionSavepoint($savepoint) {
|
||||||
$this->query("SAVEPOINT $savepoint;");
|
$this->query("SAVEPOINT $savepoint");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function transactionRollback($savepoint = false) {
|
public function transactionRollback($savepoint = false) {
|
||||||
if ($savepoint) {
|
if ($savepoint) {
|
||||||
$this->query('ROLLBACK TO ' . $savepoint . ';');
|
$this->query('ROLLBACK TO ' . $savepoint);
|
||||||
} else {
|
} else {
|
||||||
$this->query('ROLLBACK');
|
$this->query('ROLLBACK');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function transactionEnd($chain = false) {
|
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,
|
public function comparisonClause($field, $value, $exact = false, $negate = false, $caseSensitive = null,
|
||||||
|
@ -6,54 +6,54 @@
|
|||||||
* @subpackage model
|
* @subpackage model
|
||||||
*/
|
*/
|
||||||
class PDOConnector extends DBConnector {
|
class PDOConnector extends DBConnector {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should ATTR_EMULATE_PREPARES flag be used to emulate prepared statements?
|
* Should ATTR_EMULATE_PREPARES flag be used to emulate prepared statements?
|
||||||
*
|
*
|
||||||
* @config
|
* @config
|
||||||
* @var boolean
|
* @var boolean
|
||||||
*/
|
*/
|
||||||
private static $emulate_prepare = false;
|
private static $emulate_prepare = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The PDO connection instance
|
* The PDO connection instance
|
||||||
*
|
*
|
||||||
* @var PDO
|
* @var PDO
|
||||||
*/
|
*/
|
||||||
protected $pdoConnection = null;
|
protected $pdoConnection = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of the currently selected database
|
* Name of the currently selected database
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $databaseName = null;
|
protected $databaseName = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The most recent statement returned from PDODatabase->query
|
* The most recent statement returned from PDODatabase->query
|
||||||
*
|
*
|
||||||
* @var PDOStatement
|
* @var PDOStatement
|
||||||
*/
|
*/
|
||||||
protected $lastStatement = null;
|
protected $lastStatement = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of prepared statements, cached by SQL string
|
* List of prepared statements, cached by SQL string
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $cachedStatements = array();
|
protected $cachedStatements = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flush all prepared statements
|
* Flush all prepared statements
|
||||||
*/
|
*/
|
||||||
public function flushStatements() {
|
public function flushStatements() {
|
||||||
$this->cachedStatements = array();
|
$this->cachedStatements = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a prepared statement for a given SQL string, or return an already prepared version if
|
* Retrieve a prepared statement for a given SQL string, or return an already prepared version if
|
||||||
* one exists for the given query
|
* one exists for the given query
|
||||||
*
|
*
|
||||||
* @param string $sql
|
* @param string $sql
|
||||||
* @return PDOStatement
|
* @return PDOStatement
|
||||||
*/
|
*/
|
||||||
@ -66,10 +66,10 @@ class PDOConnector extends DBConnector {
|
|||||||
}
|
}
|
||||||
return $this->cachedStatements[$sql];
|
return $this->cachedStatements[$sql];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is PDO running in emulated mode
|
* Is PDO running in emulated mode
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public static function is_emulate_prepare() {
|
public static function is_emulate_prepare() {
|
||||||
@ -84,7 +84,7 @@ class PDOConnector extends DBConnector {
|
|||||||
// requested via selectDatabase
|
// requested via selectDatabase
|
||||||
$driver = $parameters['driver'] . ":";
|
$driver = $parameters['driver'] . ":";
|
||||||
$dsn = array();
|
$dsn = array();
|
||||||
|
|
||||||
// Typically this is false, but some drivers will request this
|
// Typically this is false, but some drivers will request this
|
||||||
if($selectDB) {
|
if($selectDB) {
|
||||||
// Specify complete file path immediately following driver (SQLLite3)
|
// Specify complete file path immediately following driver (SQLLite3)
|
||||||
@ -92,14 +92,14 @@ class PDOConnector extends DBConnector {
|
|||||||
$dsn[] = $parameters['filepath'];
|
$dsn[] = $parameters['filepath'];
|
||||||
} elseif(!empty($parameters['database'])) {
|
} elseif(!empty($parameters['database'])) {
|
||||||
// Some databases require a selected database at connection (SQLite3, Azure)
|
// Some databases require a selected database at connection (SQLite3, Azure)
|
||||||
if($parameters['driver'] === 'sqlsrv') {
|
if($parameters['driver'] === 'sqlsrv') {
|
||||||
$dsn[] = "Database={$parameters['database']}";
|
$dsn[] = "Database={$parameters['database']}";
|
||||||
} else {
|
} else {
|
||||||
$dsn[] = "dbname={$parameters['database']}";
|
$dsn[] = "dbname={$parameters['database']}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Syntax for sql server is slightly different
|
// Syntax for sql server is slightly different
|
||||||
if($parameters['driver'] === 'sqlsrv') {
|
if($parameters['driver'] === 'sqlsrv') {
|
||||||
$server = $parameters['server'];
|
$server = $parameters['server'];
|
||||||
@ -128,18 +128,20 @@ class PDOConnector extends DBConnector {
|
|||||||
|
|
||||||
// Connection commands to be run on every re-connection
|
// Connection commands to be run on every re-connection
|
||||||
$options = array(
|
$options = array(
|
||||||
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
|
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'
|
||||||
PDO::ATTR_EMULATE_PREPARES => self::is_emulate_prepare()
|
|
||||||
);
|
);
|
||||||
|
if(self::is_emulate_prepare()) {
|
||||||
|
$options[PDO::ATTR_EMULATE_PREPARES] = true;
|
||||||
|
}
|
||||||
|
|
||||||
// May throw a PDOException if fails
|
// May throw a PDOException if fails
|
||||||
if(empty($parameters['username']) || empty($parameters['password'])) {
|
$this->pdoConnection = new PDO(
|
||||||
$this->pdoConnection = new PDO($driver.implode(';', $dsn));
|
$driver.implode(';', $dsn),
|
||||||
} else {
|
empty($parameters['username']) ? '' : $parameters['username'],
|
||||||
$this->pdoConnection = new PDO($driver.implode(';', $dsn), $parameters['username'],
|
empty($parameters['password']) ? '' : $parameters['password'],
|
||||||
$parameters['password'], $options);
|
$options
|
||||||
}
|
);
|
||||||
|
|
||||||
// Show selected DB if requested
|
// Show selected DB if requested
|
||||||
if($this->pdoConnection && $selectDB && !empty($parameters['database'])) {
|
if($this->pdoConnection && $selectDB && !empty($parameters['database'])) {
|
||||||
$this->databaseName = $parameters['database'];
|
$this->databaseName = $parameters['database'];
|
||||||
@ -164,10 +166,10 @@ class PDOConnector extends DBConnector {
|
|||||||
public function quoteString($value) {
|
public function quoteString($value) {
|
||||||
return $this->pdoConnection->quote($value);
|
return $this->pdoConnection->quote($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes a query that doesn't return a resultset
|
* Executes a query that doesn't return a resultset
|
||||||
*
|
*
|
||||||
* @param string $sql
|
* @param string $sql
|
||||||
* @param string $sql The SQL query to execute
|
* @param string $sql The SQL query to execute
|
||||||
* @param integer $errorLevel For errors to this query, raise PHP errors
|
* @param integer $errorLevel For errors to this query, raise PHP errors
|
||||||
@ -176,7 +178,7 @@ class PDOConnector extends DBConnector {
|
|||||||
public function exec($sql, $errorLevel = E_USER_ERROR) {
|
public function exec($sql, $errorLevel = E_USER_ERROR) {
|
||||||
// Check if we should only preview this query
|
// Check if we should only preview this query
|
||||||
if ($this->previewWrite($sql)) return;
|
if ($this->previewWrite($sql)) return;
|
||||||
|
|
||||||
// Reset last statement to prevent interference in case of error
|
// Reset last statement to prevent interference in case of error
|
||||||
$this->lastStatement = null;
|
$this->lastStatement = null;
|
||||||
|
|
||||||
@ -213,7 +215,7 @@ class PDOConnector extends DBConnector {
|
|||||||
|
|
||||||
return new PDOQuery($this->lastStatement);
|
return new PDOQuery($this->lastStatement);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines the PDO::PARAM_* type for a given PHP type string
|
* Determines the PDO::PARAM_* type for a given PHP type string
|
||||||
* @param string $phpType Type of object in PHP
|
* @param string $phpType Type of object in PHP
|
||||||
@ -241,10 +243,10 @@ class PDOConnector extends DBConnector {
|
|||||||
user_error("Cannot bind parameter as it is an unsupported type ($phpType)", E_USER_ERROR);
|
user_error("Cannot bind parameter as it is an unsupported type ($phpType)", E_USER_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bind all parameters to a PDOStatement
|
* Bind all parameters to a PDOStatement
|
||||||
*
|
*
|
||||||
* @param PDOStatement $statement
|
* @param PDOStatement $statement
|
||||||
* @param array $parameters
|
* @param array $parameters
|
||||||
*/
|
*/
|
||||||
@ -259,7 +261,7 @@ class PDOConnector extends DBConnector {
|
|||||||
$phpType = $value['type'];
|
$phpType = $value['type'];
|
||||||
$value = $value['value'];
|
$value = $value['value'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check type of parameter
|
// Check type of parameter
|
||||||
$type = $this->getPDOParamType($phpType);
|
$type = $this->getPDOParamType($phpType);
|
||||||
if($type === PDO::PARAM_STR) $value = strval($value);
|
if($type === PDO::PARAM_STR) $value = strval($value);
|
||||||
@ -268,7 +270,7 @@ class PDOConnector extends DBConnector {
|
|||||||
$statement->bindValue($index+1, $value, $type);
|
$statement->bindValue($index+1, $value, $type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR) {
|
public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR) {
|
||||||
// Check if we should only preview this query
|
// Check if we should only preview this query
|
||||||
if ($this->previewWrite($sql)) return;
|
if ($this->previewWrite($sql)) return;
|
||||||
@ -276,14 +278,14 @@ class PDOConnector extends DBConnector {
|
|||||||
// Benchmark query
|
// Benchmark query
|
||||||
$self = $this;
|
$self = $this;
|
||||||
$this->lastStatement = $this->benchmarkQuery($sql, function($sql) use($parameters, $self) {
|
$this->lastStatement = $this->benchmarkQuery($sql, function($sql) use($parameters, $self) {
|
||||||
|
|
||||||
// Prepare statement
|
// Prepare statement
|
||||||
$statement = $self->getOrPrepareStatement($sql);
|
$statement = $self->getOrPrepareStatement($sql);
|
||||||
if(!$statement) return null;
|
if(!$statement) return null;
|
||||||
|
|
||||||
// Inject parameters
|
// Inject parameters
|
||||||
$self->bindParameters($statement, $parameters);
|
$self->bindParameters($statement, $parameters);
|
||||||
|
|
||||||
// Safely execute the statement
|
// Safely execute the statement
|
||||||
$statement->execute($parameters);
|
$statement->execute($parameters);
|
||||||
return $statement;
|
return $statement;
|
||||||
@ -298,17 +300,17 @@ class PDOConnector extends DBConnector {
|
|||||||
|
|
||||||
return new PDOQuery($this->lastStatement);
|
return new PDOQuery($this->lastStatement);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if a resource has an attached error
|
* Determine if a resource has an attached error
|
||||||
*
|
*
|
||||||
* @param PDOStatement|PDO $resource the resource to check
|
* @param PDOStatement|PDO $resource the resource to check
|
||||||
* @return boolean Flag indicating true if the resource has an error
|
* @return boolean Flag indicating true if the resource has an error
|
||||||
*/
|
*/
|
||||||
protected function hasError($resource) {
|
protected function hasError($resource) {
|
||||||
// No error if no resource
|
// No error if no resource
|
||||||
if(empty($resource)) return false;
|
if(empty($resource)) return false;
|
||||||
|
|
||||||
// If the error code is empty the statement / connection has not been run yet
|
// If the error code is empty the statement / connection has not been run yet
|
||||||
$code = $resource->errorCode();
|
$code = $resource->errorCode();
|
||||||
if(empty($code)) return false;
|
if(empty($code)) return false;
|
||||||
|
@ -27,10 +27,6 @@ class PDOQuery extends SS_Query {
|
|||||||
$statement->closeCursor();
|
$statement->closeCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __destruct() {
|
|
||||||
$this->statement->closeCursor();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function seek($row) {
|
public function seek($row) {
|
||||||
$this->rowNum = $row - 1;
|
$this->rowNum = $row - 1;
|
||||||
return $this->nextRecord();
|
return $this->nextRecord();
|
||||||
|
Loading…
Reference in New Issue
Block a user