2013-06-21 00:32:08 +02:00
|
|
|
<?php
|
|
|
|
|
2016-06-15 06:03:16 +02:00
|
|
|
namespace SilverStripe\ORM\Connect;
|
|
|
|
|
2016-08-19 00:51:35 +02:00
|
|
|
use SilverStripe\Core\Config\Config;
|
|
|
|
use SilverStripe\View\Parsers\SQLFormatter;
|
2016-06-15 06:03:16 +02:00
|
|
|
|
2013-06-21 00:32:08 +02:00
|
|
|
/**
|
|
|
|
* Represents an object responsible for wrapping DB connector api
|
|
|
|
*/
|
|
|
|
abstract class DBConnector {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* List of operations to treat as write
|
2015-06-16 05:04:20 +02:00
|
|
|
* Implicitly includes all ddl_operations
|
2013-06-21 00:32:08 +02:00
|
|
|
*
|
|
|
|
* @config
|
|
|
|
* @var array
|
|
|
|
*/
|
2015-06-16 05:04:20 +02:00
|
|
|
private static $write_operations = array('insert', 'update', 'delete', 'replace');
|
2016-01-06 00:34:58 +01:00
|
|
|
|
2015-06-16 05:04:20 +02:00
|
|
|
/**
|
|
|
|
* List of operations to treat as DDL
|
|
|
|
*
|
|
|
|
* @config
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
private static $ddl_operations = array('alter', 'drop', 'create', 'truncate');
|
2013-06-21 00:32:08 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Error handler for database errors.
|
|
|
|
* All database errors will call this function to report the error. It isn't a static function;
|
|
|
|
* it will be called on the object itself and as such can be overridden in a subclass.
|
|
|
|
* Subclasses should run all errors through this function.
|
|
|
|
*
|
|
|
|
* @todo hook this into a more well-structured error handling system.
|
|
|
|
* @param string $msg The error message.
|
|
|
|
* @param integer $errorLevel The level of the error to throw.
|
|
|
|
* @param string $sql The SQL related to this query
|
|
|
|
* @param array $parameters Parameters passed to the query
|
2015-06-16 05:04:20 +02:00
|
|
|
* @throws SS_DatabaseException
|
2013-06-21 00:32:08 +02:00
|
|
|
*/
|
|
|
|
protected function databaseError($msg, $errorLevel = E_USER_ERROR, $sql = null, $parameters = array()) {
|
|
|
|
// Prevent errors when error checking is set at zero level
|
|
|
|
if(empty($errorLevel)) return;
|
|
|
|
|
|
|
|
// Format query if given
|
|
|
|
if (!empty($sql)) {
|
|
|
|
$formatter = new SQLFormatter();
|
|
|
|
$formattedSQL = $formatter->formatPlain($sql);
|
|
|
|
$msg = "Couldn't run query:\n\n{$formattedSQL}\n\n{$msg}";
|
|
|
|
}
|
|
|
|
|
|
|
|
if($errorLevel === E_USER_ERROR) {
|
|
|
|
// Treating errors as exceptions better allows for responding to errors
|
|
|
|
// in code, such as credential checking during installation
|
|
|
|
throw new SS_DatabaseException($msg, 0, null, $sql, $parameters);
|
|
|
|
} else {
|
|
|
|
user_error($msg, $errorLevel);
|
|
|
|
}
|
|
|
|
}
|
2016-01-06 00:34:58 +01:00
|
|
|
|
2013-06-21 00:32:08 +02:00
|
|
|
/**
|
2015-06-16 05:04:20 +02:00
|
|
|
* Determine if this SQL statement is a destructive operation (write or ddl)
|
2016-01-06 00:34:58 +01:00
|
|
|
*
|
2015-06-16 05:04:20 +02:00
|
|
|
* @param string $sql
|
|
|
|
* @return bool
|
2013-06-21 00:32:08 +02:00
|
|
|
*/
|
2015-06-16 05:04:20 +02:00
|
|
|
public function isQueryMutable($sql) {
|
|
|
|
$operations = array_merge(
|
|
|
|
Config::inst()->get(get_class($this), 'write_operations'),
|
|
|
|
Config::inst()->get(get_class($this), 'ddl_operations')
|
|
|
|
);
|
|
|
|
return $this->isQueryType($sql, $operations);
|
2013-06-21 00:32:08 +02:00
|
|
|
}
|
2016-01-06 00:34:58 +01:00
|
|
|
|
2013-06-21 00:32:08 +02:00
|
|
|
/**
|
2015-06-16 05:04:20 +02:00
|
|
|
* Determine if this SQL statement is a DDL operation
|
2016-01-06 00:34:58 +01:00
|
|
|
*
|
2015-06-16 05:04:20 +02:00
|
|
|
* @param string $sql
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function isQueryDDL($sql) {
|
|
|
|
$operations = Config::inst()->get(get_class($this), 'ddl_operations');
|
|
|
|
return $this->isQueryType($sql, $operations);
|
|
|
|
}
|
2016-01-06 00:34:58 +01:00
|
|
|
|
2015-06-16 05:04:20 +02:00
|
|
|
/**
|
|
|
|
* Determine if this SQL statement is a write operation
|
|
|
|
* (alters content but not structure)
|
2016-01-06 00:34:58 +01:00
|
|
|
*
|
2015-06-16 05:04:20 +02:00
|
|
|
* @param string $sql
|
|
|
|
* @return bool
|
2013-06-21 00:32:08 +02:00
|
|
|
*/
|
2015-06-16 05:04:20 +02:00
|
|
|
public function isQueryWrite($sql) {
|
|
|
|
$operations = Config::inst()->get(get_class($this), 'write_operations');
|
|
|
|
return $this->isQueryType($sql, $operations);
|
|
|
|
}
|
2016-01-06 00:34:58 +01:00
|
|
|
|
2015-06-16 05:04:20 +02:00
|
|
|
/**
|
|
|
|
* Determine if a query is of the given type
|
2016-01-06 00:34:58 +01:00
|
|
|
*
|
2015-06-16 05:04:20 +02:00
|
|
|
* @param string $sql Raw SQL
|
|
|
|
* @param string|array $type Type or list of types (first word in the query). Must be lowercase
|
2016-08-19 00:51:35 +02:00
|
|
|
* @return bool
|
2015-06-16 05:04:20 +02:00
|
|
|
*/
|
|
|
|
protected function isQueryType($sql, $type) {
|
|
|
|
if(!preg_match('/^(?<operation>\w+)\b/', $sql, $matches)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
$operation = $matches['operation'];
|
|
|
|
if(is_array($type)) {
|
|
|
|
return in_array(strtolower($operation), $type);
|
2013-06-21 00:32:08 +02:00
|
|
|
} else {
|
2015-06-16 05:04:20 +02:00
|
|
|
return strcasecmp($sql, $type) === 0;
|
2013-06-21 00:32:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Extracts only the parameter values for error reporting
|
|
|
|
*
|
|
|
|
* @param array $parameters
|
|
|
|
* @return array List of parameter values
|
|
|
|
*/
|
|
|
|
protected function parameterValues($parameters) {
|
|
|
|
$values = array();
|
|
|
|
foreach($parameters as $value) {
|
|
|
|
$values[] = is_array($value) ? $value['value'] : $value;
|
|
|
|
}
|
|
|
|
return $values;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Link this connector to the database given the specified parameters
|
|
|
|
* Will throw an exception rather than return a success state.
|
|
|
|
* The connector should not select the database once connected until
|
|
|
|
* explicitly called by selectDatabase()
|
|
|
|
*
|
|
|
|
* @param array $parameters List of parameters such as
|
|
|
|
* <ul>
|
|
|
|
* <li>type</li>
|
|
|
|
* <li>server</li>
|
|
|
|
* <li>username</li>
|
|
|
|
* <li>password</li>
|
|
|
|
* <li>database</li>
|
|
|
|
* <li>path</li>
|
|
|
|
* </ul>
|
|
|
|
* @param boolean $selectDB By default database selection should be
|
|
|
|
* handled by the database controller (to enable database creation on the
|
|
|
|
* fly if necessary), but some interfaces require that the database is
|
|
|
|
* specified during connection (SQLite, Azure, etc).
|
|
|
|
*/
|
|
|
|
abstract public function connect($parameters, $selectDB = false);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Query for the version of the currently connected database
|
|
|
|
*
|
|
|
|
* @return string Version of this database
|
|
|
|
*/
|
|
|
|
abstract public function getVersion();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given a value escape this for use in a query for the current database
|
|
|
|
* connector. Note that this does not quote the value.
|
|
|
|
*
|
|
|
|
* @param string $value The value to be escaped
|
|
|
|
* @return string The appropritaely escaped string for value
|
|
|
|
*/
|
|
|
|
abstract public function escapeString($value);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given a value escape and quote this appropriately for the current
|
|
|
|
* database connector.
|
|
|
|
*
|
|
|
|
* @param string $value The value to be injected into a query
|
|
|
|
* @return string The appropriately escaped and quoted string for $value
|
|
|
|
*/
|
|
|
|
abstract public function quoteString($value);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Executes the following query with the specified error level.
|
|
|
|
* Implementations of this function should respect previewWrite and benchmarkQuery
|
|
|
|
*
|
|
|
|
* @see http://php.net/manual/en/errorfunc.constants.php
|
|
|
|
* @param string $sql The SQL query to execute
|
|
|
|
* @param integer $errorLevel For errors to this query, raise PHP errors
|
|
|
|
* using this error level.
|
|
|
|
*/
|
|
|
|
abstract public function query($sql, $errorLevel = E_USER_ERROR);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Execute the given SQL parameterised query with the specified arguments
|
|
|
|
*
|
|
|
|
* @param string $sql The SQL query to execute. The ? character will denote parameters.
|
|
|
|
* @param array $parameters An ordered list of arguments.
|
|
|
|
* @param int $errorLevel The level of error reporting to enable for the query
|
|
|
|
* @return SS_Query
|
|
|
|
*/
|
|
|
|
abstract public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Select a database by name
|
|
|
|
*
|
|
|
|
* @param string $name Name of database
|
|
|
|
* @return boolean Flag indicating success
|
|
|
|
*/
|
|
|
|
abstract public function selectDatabase($name);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves the name of the currently selected database
|
|
|
|
*
|
|
|
|
* @return string Name of the database, or null if none selected
|
|
|
|
*/
|
|
|
|
abstract public function getSelectedDatabase();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* De-selects the currently selected database
|
|
|
|
*/
|
|
|
|
abstract public function unloadDatabase();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves the last error generated from the database connection
|
|
|
|
*
|
|
|
|
* @return string The error message
|
|
|
|
*/
|
|
|
|
abstract public function getLastError();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determines the last ID generated from the specified table.
|
|
|
|
* Note that some connectors may not be able to return $table specific responses,
|
|
|
|
* and this parameter may be ignored.
|
|
|
|
*
|
|
|
|
* @param string $table The target table to return the last generated ID for
|
|
|
|
* @return integer ID value
|
|
|
|
*/
|
|
|
|
abstract public function getGeneratedID($table);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determines the number of affected rows from the last SQL query
|
|
|
|
*
|
|
|
|
* @return integer Number of affected rows
|
|
|
|
*/
|
|
|
|
abstract public function affectedRows();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determines if we are connected to a server AND have a valid database
|
|
|
|
* selected.
|
|
|
|
*
|
|
|
|
* @return boolean Flag indicating that a valid database is connected
|
|
|
|
*/
|
|
|
|
abstract public function isActive();
|
|
|
|
}
|