From f0b9103de4f1bce769aeaf2907c7b565ec738df2 Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Fri, 14 Sep 2007 01:28:16 +0000 Subject: [PATCH] pkrenn: Various database updates (merged from branches/gsoc) git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@41704 467b73ca-7a2a-4603-9d3b-597d59a354a9 --- core/model/DB.php | 27 +++- core/model/Database.php | 13 +- core/model/DatabaseAdmin.php | 9 +- core/model/MySQLDatabase.php | 13 +- core/model/PDODatabase.php | 265 ++++++++++++++++++++++++++++++----- 5 files changed, 282 insertions(+), 45 deletions(-) diff --git a/core/model/DB.php b/core/model/DB.php index 2b47d140f..fda6f7b54 100755 --- a/core/model/DB.php +++ b/core/model/DB.php @@ -30,7 +30,7 @@ class DB { */ static function setConn($globalConn) { DB::$globalConn = $globalConn; - } + } /** * Get the global database connection. @@ -48,11 +48,24 @@ class DB { */ static function connect($databaseConfig) { if(!$databaseConfig['type']) user_error("DB::connect: Not passed a valid database config", E_USER_ERROR); - $dbClass = $databaseConfig['type']; - $conn = new $dbClass($databaseConfig); + if ($databaseConfig['pdo']) { // TODO:pkrenn_remove + $conn = new PDODatabase($databaseConfig); + } else { // TODO:pkrenn_remove begin + $dbClass = $databaseConfig['type']; + $conn = new $dbClass($databaseConfig); + } // TODO:pkrenn_remove end DB::setConn($conn); } + /** + * Build the connection string from input. + * @param array $parameters The connection details. + * @return string $connect The connection string. + **/ + public function getConnect($parameters) { + return DB::$globalConn->getConnect($parameters); + } + /** * Execute the given SQL query. * @param string $sql The SQL query to execute @@ -106,10 +119,14 @@ class DB { * Create the database and connect to it. This can be called if the * initial database connection is not successful because the database * does not exist. + * @param string $connect Connection string + * @param string $username Database username + * @param string $password Database Password + * @param string $database Database to which to create * @return boolean Returns true if successful */ - static function createDatabase() { - return DB::$globalConn->createDatabase(); + static function createDatabase($connect, $username, $password, $database) { + return DB::$globalConn->createDatabase($connect, $username, $password, $database); } /** diff --git a/core/model/Database.php b/core/model/Database.php index 189a59997..d58fe4522 100755 --- a/core/model/Database.php +++ b/core/model/Database.php @@ -49,9 +49,20 @@ abstract class Database extends Object { * Create the database and connect to it. This can be called if the * initial database connection is not successful because the database * does not exist. + * @param string $connect Connection string + * @param string $username Database username + * @param string $password Database Password + * @param string $database Database to which to create * @return boolean Returns true if successful */ - abstract function createDatabase(); + abstract function createDatabase($connect, $username, $password, $database); + + /** + * Build the connection string from input + * @param array $parameters The connection details + * @return string $connect The connection string + **/ + abstract function getConnect($parameters); /** * Create a new table. diff --git a/core/model/DatabaseAdmin.php b/core/model/DatabaseAdmin.php index aab9b58b1..6c3c820e6 100644 --- a/core/model/DatabaseAdmin.php +++ b/core/model/DatabaseAdmin.php @@ -135,7 +135,12 @@ class DatabaseAdmin extends Controller { if(!$quiet) { echo '

Creating database

'; } - DB::createDatabase(); + $parameters = $_REQUEST['db']; + $connect = DB::getConnect($parameters); + $username = $parameters[username]; + $password = $parameters[password]; + $database = $parameters[database]; + DB::createDatabase($connect, $username, $password, $database); // ManifestBuilder::compileManifest(); } @@ -432,4 +437,4 @@ class DatabaseAdmin extends Controller { } -?> \ No newline at end of file +?> diff --git a/core/model/MySQLDatabase.php b/core/model/MySQLDatabase.php index 4134c6b7e..1921edd8e 100644 --- a/core/model/MySQLDatabase.php +++ b/core/model/MySQLDatabase.php @@ -46,6 +46,13 @@ class MySQLDatabase extends Database { parent::__construct(); } + /** + * Not implemented, needed for PDO + */ + public function getConnect($parameters) { + return null; + } + /** * Returns true if this database supports collations * @return boolean @@ -109,10 +116,10 @@ class MySQLDatabase extends Database { } public function isActive() { - return $this->active ? true : false; + return $this->active ? true : false; } - public function createDatabase() { + public function createDatabase($connect, $username, $password, $db) { $this->query("CREATE DATABASE $this->database"); $this->query("USE $this->database"); @@ -361,7 +368,7 @@ class MySQLDatabase extends Database { $table = strtolower(reset($record)); $tables[$table] = $table; } - return isset($tables) ? $tables : null; + return isset($tables) ? $tables : null; } /** diff --git a/core/model/PDODatabase.php b/core/model/PDODatabase.php index 005444615..c5c250f1e 100644 --- a/core/model/PDODatabase.php +++ b/core/model/PDODatabase.php @@ -1,11 +1,30 @@
  • database: The database to connect with
  • *
  • server: The server, eg, localhost
  • + *
  • port: The port on which the server is listening (optional)
  • + *
  • instance: Instance of the server, MS SQL only (optional)
  • *
  • username: The username to log on with
  • *
  • password: The password to log on with
  • *
  • database: The database to connect to
  • */ public function __construct($parameters) { - switch ($parameters['database']) { - case "MySQL": - $connect = 'mysql:host=' . $parameters['server'] . ';dbname=' . $parameters['database']; - break; - case "PostgreSQL": - $connect = 'pgsql:host=' . $parameters['server'] . ';port=5432;dbname=' . $parameters['database']; - break; - case "MSSQL": - $connect = 'mssql:host=' . $parameters['server'] . ';dbname=' . $parameters['database']; - break; - default: $this->databaseError("Database not available"); + $connect = PDODatabase::getConnect($parameters); + $connectWithDB = $connect . ';dbname=' . $parameters['database']; + try { // Try connect to the database, if it does not exist, create it + $this->dbConn = new PDO($connectWithDB, $parameters['username'], $parameters['password']); + } catch (PDOException $e) { + if (!self::createDatabase($connect, $parameters['username'], $parameters['password'], $parameters['database'])) { + $this->databaseError("Could not connect to the database, make sure the server is available and user credentials are correct"); + } } - $this->dbConn = new PDO($connect, $parameters['username'], $parameters['password']); - $this->database = $parameters['database']; - if(!$this->dbConn) $this->databaseError("Could connect to MySQL database"); parent::__construct(); } + /** + * Build the connection string from input. + * @param array $parameters The connection details. + * @return string $connect The connection string. + **/ + public function getConnect($parameters) { + switch ($parameters['type']) { + case "mysql": + $port = '3306'; + $type = 'mysql'; + $instance = ''; + break; + case "postgresql": + $port = '5432'; + $type = 'pgsql'; + $instance = ''; + break; + case "mssql": + $port = '1433'; + if (isset($parameters['instance']) && $parameters['instance'] != '') { + $instance = '\\' . $parameters['instance']; + } else { + $instance = ''; + } + $type = 'mssql'; + break; + default: + $this->databaseError("This database server is not available"); + } + if (isset($parameters['port']) && is_numeric($parameters['port'])) { + $port = $parameters['port']; + } + $connect = $type . ':host=' . $parameters['server'] . $instance . ';port=' . $port; + return $connect; + } + /** * Returns true if this database supports collations */ public function supportsCollations() { + $collations = false; + switch (PDO::ATTR_DRIVER_NAME) { + case "pgsql": // Generally supported in PostgreSQL (supported versions) + case "mssql": // Generally supported in MS SQL (supported versions) + $collations = true; + break; + case "mysql": + if ($this->getVersion() >= 4.1) { // Supported in MySQL since 4.1 + $collations = true; + } + break; + } + return $collations; } - //private $mysqlVersion; + /** + * Get the database version. + * @return float + */ public function getVersion() { + switch ($type) { + case "mysql": + case "postgresql": + $query = "SELECT VERSION()"; + break; + case "mssql": + $query = "SELECT @@VERSION"; + break; + } + $getData = $dbConn->prepare($query); + $getData->execute(); + $dbVersion = $getData->fetchColumn(); + $version = ereg_replace("([A-Za-z-])", "", $dbVersion); + return substr(trim($version), 0, 3); // Just get the major and minor version } public function query($sql, $errorLevel = E_USER_ERROR) { + if(isset($_REQUEST['previewwrite']) && in_array(strtolower(substr($sql,0,6)), array('insert','update'))) { + echo "

    Will execute: $sql

    "; + return; + } + //Debug::backtrace(); + if(isset($_REQUEST['showqueries'])) { + Debug::message("\n" . $sql . "\n"); + $starttime = microtime(true); + } + + $query = $dbConn->prepare($sql); + $handle = $query->execute(); // Execute and save the return value (true or false) + $result = $query->fetchAll(); // Get the result itself + + if(isset($_REQUEST['showqueries'])) { + $duration = microtime(true) - $starttime; + Debug::message("\n" . $duration . "\n"); + } + + if(!$handle && $errorLevel) { + $error = $query->errorInfo(); + $this->databaseError("Couldn't run query: $sql | " . $error[2], $errorLevel); + } + return new PDOQuery($result); } - public function getGeneratedID() { - } - public function getNextID($table) { - } - public function isActive() { - } - public function createDatabase() { - } + /** - * Create a new table with an integer primary key called ID. + * Get the ID for the next new record for the table. + * @var string $table The name od the table. + * @return int */ - public function createTable($tableName) { + public function getNextID($table) { + $sql = "SELECT MAX(ID)+1 FROM :table"; + $create->bindParam(":table", $table); + $query = $dbConn->prepare($sql); + $handle = $query->execute(); + $result = $query->fetchColumn(); + return $handle ? $result : 1; + } + + public function isActive() { + return $this->active ? true : false; + } + + /** + * Create the database and connect to it. This can be called if the + * initial database connection is not successful because the database + * does not exist. + * @param string $connect Connection string + * @param string $username Database username + * @param string $password Database Password + * @param string $database Database to which to create + * @return boolean Returns true if successful + */ + public function createDatabase($connect, $username, $password, $database) { + try { + $dbConn = new PDO($connect, $username, $password); + $create = $dbConn->prepare("CREATE DATABASE :database"); + $create->bindParam(":database", $database); + $create->execute(); + $this->active = true; + } catch (PDOException $e) { + $this->databaseError($e->getMessage()); + return false; + } + return true; } /** * Create a new table with an integer primary key called ID. + * @var string $tableName The name of the table. + * @return void. + */ + public function createTable($tableName, $fields = null, $indexes = null) { + $fieldSchemas = $indexSchemas = ""; + if ($fields) { + foreach($fields as $k => $v) $fieldSchemas .= "`$k` $v,\n"; + } + if ($indexes) { + foreach($indexes as $k => $v) $fieldSchemas .= $this->getIndexSqlDefinition($k, $v) . ",\n"; + } + + switch ($parameters['type']) { + case "mysql": $create = $dbConn->prepare("CREATE TABLE :tableName (ID INT(11) NOT NULL AUTO_INCREMENT, $fieldSchemas $indexSchemas PRIMARY KEY (ID)) TYPE=MyISAM"); + break; + case "postgresql": $create = $dbConn->prepare("CREATE TABLE :tableName (ID SERIAL, $fieldSchemas $indexSchemas PRIMARY KEY (ID))"); + break; + case "mssql": $create = $dbConn->prepare("CREATE TABLE :tableName (ID INT(11) IDENTITY(1,1), $fieldSchemas $indexSchemas PRIMARY KEY (ID))"); + break; + default: + $this->databaseError("This database server is not available"); + } + $create->bindParam(":tableName", $tableName); + $create->execute(); + } + + public function alterTable($table, $newFields, $newIndexes, $alteredFields, $alteredIndexes) { + $fieldSchemas = $indexSchemas = ""; + + if ($newFields) { + foreach($newFields as $k => $v) $alterList[] .= "ADD `$k` $v"; + } + if ($newIndexes) { + foreach($newIndexes as $k => $v) $alterList[] .= "ADD " . $this->getIndexSqlDefinition($k, $v) . ",\n"; + } + if ($alteredFields) { + foreach($alteredFields as $k => $v) $alterList[] .= "CHANGE `$k` `$k` $v"; + } + if ($alteredIndexes) foreach($alteredIndexes as $k => $v) { + $alterList[] .= "DROP INDEX `$k`"; + $alterList[] .= "ADD ". $this->getIndexSqlDefinition($k, $v); + } + + $alterations = implode(",\n", $alterList); + $this->query("ALTER TABLE `$tableName` " . $alterations); + } + /** + * Rename an existing table, the TO is necessary for PostgreSQL and MS SQL. + * @var string $oldTableName The name of the existing table. + * @var string $newTableName How the table should be named from now on. + * @return void. */ public function renameTable($oldTableName, $newTableName) { + $query = "ALTER TABLE :oldTableName RENAME TO :newTableName"; + $create->bindParam(":oldTableName", $oldTableName); + $create->bindParam(":newTableName", $newTableName); + $create = $dbConn->prepare($query); + $create->execute(); } /** @@ -80,7 +270,13 @@ class PDODatabase extends Database { * Add the given field to the given table. */ public function createField($tableName, $fieldName, $fieldSpec) { + $create = $dbConn->prepare("ALTER TABLE :tableName ADD :fieldName :fieldSpec"); + $create->bindParam(":tableName", $tableName); + $create->bindParam(":fieldName", $fieldName); + $create->bindParam(":fieldSpec", $fieldSpec); + $create->execute(); } + /** * Change the database type of the given field */ @@ -117,32 +313,33 @@ class PDODatabase extends Database { } /** - * A result-set from a MySQL database. + * A result-set from a database query (array). */ -class MySQLQuery extends Query { - private $database; - private $handle; +class PDOQuery extends Query { + private $result; /** * Hook the result-set given into a Query class, suitable for use by sapphire. - * @param database The database object that created this query. - * @param handle the internal mysql handle that is points to the resultset. + * @param array $result The array of all returned values. */ - public function __construct(MySQLDatabase $database, $handle) { - $this->database = $database; - $this->handle = $handle; + public function __construct(PDODatabase $result) { + $this->result = $result; parent::__construct(); } public function __destroy() { + $this->result = null; } public function seek($row) { + return in_array($row, $this->result); } public function numRecords() { + return count($this->result); } public function nextRecord() { + } }