ENHANCEMENT Added DataObject::$create_table_options to pass through special options to Database->requireTable(). Contains a keyed array by database driver. Example use is specifying different storage engines for MySQL: array('MySQLDatabase'=>'ENGINE=InnoDB')

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@77160 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Ingo Schommer 2009-05-19 03:55:14 +00:00
parent 0b9edf2a39
commit 336bce8bf8
5 changed files with 109 additions and 18 deletions

View File

@ -187,9 +187,10 @@ class DB {
* - true: Create a single column index on the field named the same as the index.
* - array('fields' => array('A','B','C'), 'type' => 'index/unique/fulltext'): This gives you full
* control over the index.
* @param string $options SQL statement to append to the CREATE TABLE call.
*/
static function requireTable($table, $fieldSchema = null, $indexSchema = null, $hasAutoIncPK=true) {
return DB::$globalConn->requireTable($table, $fieldSchema, $indexSchema, $hasAutoIncPK);
static function requireTable($table, $fieldSchema = null, $indexSchema = null, $hasAutoIncPK=true, $options = null) {
return DB::$globalConn->requireTable($table, $fieldSchema, $indexSchema, $hasAutoIncPK, $options);
}
/**

View File

@ -2666,7 +2666,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
if($fields) {
$hasAutoIncPK = ($this->class == ClassInfo::baseDataClass($this->class));
DB::requireTable($this->class, $fields, $indexes, $hasAutoIncPK);
DB::requireTable($this->class, $fields, $indexes, $hasAutoIncPK, $this->stat('create_table_options'));
} else {
DB::dontRequireTable($this->class);
}
@ -3019,6 +3019,24 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
"Created" => "SSDatetime",
"Title" => 'Text',
);
/**
* Specify custom options for a CREATE TABLE call.
* Can be used to specify a custom storage engine for specific database table.
* All options have to be keyed for a specific database implementation,
* identified by their class name (extending from {@link Database}).
*
* <code>
* array(
* 'MySQLDatabase' => 'ENGINE=MyISAM'
* )
* </code>
*
* @var array
*/
static $create_table_options = array(
'MySQLDatabase' => 'ENGINE=MyISAM'
);
/**
* If a field is in this array, then create a database index

View File

@ -71,9 +71,13 @@ abstract class Database extends Object {
/**
* Create a new table.
* The table will have a single field - the integer key ID.
* @param string $table Name of table to create.
* @param string $table
* @param array $fields
* @param array $indexes
* @param string $driver
* @param array $options
*/
abstract function createTable($table, $fields = null, $indexes = null);
abstract function createTable($table, $fields = null, $indexes = null, $options = null);
/**
* Alter a table's schema.
@ -177,12 +181,12 @@ abstract class Database extends Object {
foreach($this->schemaUpdateTransaction as $tableName => $changes) {
switch($changes['command']) {
case 'create':
$this->createTable($tableName, $changes['newFields'], $changes['newIndexes']);
$this->createTable($tableName, $changes['newFields'], $changes['newIndexes'], $changes['options']);
break;
case 'alter':
$this->alterTable($tableName, $changes['newFields'], $changes['newIndexes'],
$changes['alteredFields'], $changes['alteredIndexes']);
$changes['alteredFields'], $changes['alteredIndexes'], $changes['alteredOptions']);
break;
}
}
@ -191,9 +195,23 @@ abstract class Database extends Object {
// Transactional schema altering functions - they don't do anyhting except for update schemaUpdateTransaction
function transCreateTable($table) {
$this->schemaUpdateTransaction[$table] = array('command' => 'create', 'newFields' => array(), 'newIndexes' => array());
/**
* @param string $table
* @param string $options
*/
function transCreateTable($table, $options = null) {
$this->schemaUpdateTransaction[$table] = array('command' => 'create', 'newFields' => array(), 'newIndexes' => array(), 'options' => $options);
}
/**
* @param string $table
* @param array $options
*/
function transAlterTable($table, $options) {
$this->transInitTable($table);
$this->schemaUpdateTransaction[$table]['alteredOptions'] = $options;
}
function transCreateField($table, $field, $schema) {
$this->transInitTable($table);
$this->schemaUpdateTransaction[$table]['newFields'][$field] = $schema;
@ -223,6 +241,7 @@ abstract class Database extends Object {
'newIndexes' => array(),
'alteredFields' => array(),
'alteredIndexes' => array(),
'alteredOptions' => ''
);
}
}
@ -231,16 +250,36 @@ abstract class Database extends Object {
/**
* Generate the following table in the database, modifying whatever already exists
* as necessary.
* @todo Change detection for CREATE TABLE $options other than "Engine"
*
* @param string $table The name of the table
* @param string $fieldSchema A list of the fields to create, in the same form as DataObject::$db
* @param string $indexSchema A list of indexes to create. See {@link requireIndex()}
* @param array $options
*/
function requireTable($table, $fieldSchema = null, $indexSchema = null, $hasAutoIncPK=true) {
function requireTable($table, $fieldSchema = null, $indexSchema = null, $hasAutoIncPK=true, $options = null) {
if(!isset($this->tableList[strtolower($table)])) {
$this->transCreateTable($table);
$this->transCreateTable($table, $options);
Database::alteration_message("Table $table: created","created");
} else {
$this->checkAndRepairTable($table);
$this->checkAndRepairTable($table, $options);
// Check if options changed
if($options && isset($options[$this->class])) {
$tableOptionsChanged = false;
if(preg_match('/ENGINE=([^\s]*)/', $options[$this->class], $alteredEngineMatches)) {
$alteredEngine = $alteredEngineMatches[1];
$tableStatus = DB::query(sprintf(
'SHOW TABLE STATUS WHERE "Name" = \'%s\'',
$table
))->first();
$tableOptionsChanged = ($tableStatus['Engine'] != $alteredEngine);
}
if($tableOptionsChanged) {
$this->transAlterTable($table, $options);
}
}
}
//DB ABSTRACTION: we need to convert this to a db-specific version:
@ -253,7 +292,7 @@ abstract class Database extends Object {
$fieldObj->setTable($table);
$fieldObj->requireField();
}
}
}
// Create custom indexes
if($indexSchema) {

View File

@ -190,18 +190,25 @@ class MySQLDatabase extends Database {
return $this->query("SHOW DATABASES")->column();
}
public function createTable($tableName, $fields = null, $indexes = null) {
/**
* @param string $table
* @param array $fields
* @param array $indexes
* @param string $options
*/
public function createTable($table, $fields = null, $indexes = null, $options = null) {
$fieldSchemas = $indexSchemas = "";
$addOptions = (isset($options[$this->class])) ? $options[$this->class] : null;
if(!isset($fields['ID'])) $fields['ID'] = "int(11) not null auto_increment";
if($fields) foreach($fields as $k => $v) $fieldSchemas .= "\"$k\" $v,\n";
if($indexes) foreach($indexes as $k => $v) $indexSchemas .= $this->getIndexSqlDefinition($k, $v) . ",\n";
$this->query("CREATE TABLE \"$tableName\" (
$this->query("CREATE TABLE \"$table\" (
$fieldSchemas
$indexSchemas
primary key (ID)
) TYPE=MyISAM");
) {$addOptions}");
}
/**
@ -211,9 +218,11 @@ class MySQLDatabase extends Database {
* @param $newIndexes New indexes, a map of index name => index type
* @param $alteredFields Updated fields, a map of field name => field schema
* @param $alteredIndexes Updated indexes, a map of index name => index type
* @param $alteredOptions
*/
public function alterTable($tableName, $newFields = null, $newIndexes = null, $alteredFields = null, $alteredIndexes = null) {
public function alterTable($tableName, $newFields = null, $newIndexes = null, $alteredFields = null, $alteredIndexes = null, $alteredOptions = null) {
$fieldSchemas = $indexSchemas = "";
$alterList = array();
if($newFields) foreach($newFields as $k => $v) $alterList[] .= "ADD \"$k\" $v";
if($newIndexes) foreach($newIndexes as $k => $v) $alterList[] .= "ADD " . $this->getIndexSqlDefinition($k, $v);
@ -225,6 +234,14 @@ class MySQLDatabase extends Database {
$alterations = implode(",\n", $alterList);
$this->query("ALTER TABLE \"$tableName\" $alterations");
if($alteredOptions && isset($alteredOptions[$this->class])) {
$this->query(sprintf("ALTER TABLE \"%s\" %s", $tableName, $alteredOptions[$this->class]));
Database::alteration_message(
sprintf("Table %s options changed: %s", $tableName, $alteredOptions[$this->class]),
"changed"
);
}
}
public function renameTable($oldTableName, $newTableName) {

View File

@ -45,9 +45,25 @@ class DatabaseTest extends SapphireTest {
self::create_temp_db();
}
function testMySQLCreateTableOptions() {
if(DB::getConn() instanceof MySQLDatabase) {
$ret = DB::query(sprintf(
'SHOW TABLE STATUS WHERE "Name" = \'%s\'',
'DatabaseTest_MyObject'
))->first();
$this->assertEquals($ret['Engine'],'InnoDB',
"MySQLDatabase tables can be changed to InnoDB through DataObject::\$create_table_options"
);
}
}
}
class DatabaseTest_MyObject extends DataObject implements TestOnly {
static $create_table_options = array('MySQLDatabase' => 'ENGINE=InnoDB');
static $db = array(
'MyField' => 'Varchar'
);