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. * - 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 * - array('fields' => array('A','B','C'), 'type' => 'index/unique/fulltext'): This gives you full
* control over the index. * 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) { static function requireTable($table, $fieldSchema = null, $indexSchema = null, $hasAutoIncPK=true, $options = null) {
return DB::$globalConn->requireTable($table, $fieldSchema, $indexSchema, $hasAutoIncPK); return DB::$globalConn->requireTable($table, $fieldSchema, $indexSchema, $hasAutoIncPK, $options);
} }
/** /**

View File

@ -2666,7 +2666,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
if($fields) { if($fields) {
$hasAutoIncPK = ($this->class == ClassInfo::baseDataClass($this->class)); $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 { } else {
DB::dontRequireTable($this->class); DB::dontRequireTable($this->class);
} }
@ -3019,6 +3019,24 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
"Created" => "SSDatetime", "Created" => "SSDatetime",
"Title" => 'Text', "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 * 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. * Create a new table.
* The table will have a single field - the integer key ID. * 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. * Alter a table's schema.
@ -177,12 +181,12 @@ abstract class Database extends Object {
foreach($this->schemaUpdateTransaction as $tableName => $changes) { foreach($this->schemaUpdateTransaction as $tableName => $changes) {
switch($changes['command']) { switch($changes['command']) {
case 'create': case 'create':
$this->createTable($tableName, $changes['newFields'], $changes['newIndexes']); $this->createTable($tableName, $changes['newFields'], $changes['newIndexes'], $changes['options']);
break; break;
case 'alter': case 'alter':
$this->alterTable($tableName, $changes['newFields'], $changes['newIndexes'], $this->alterTable($tableName, $changes['newFields'], $changes['newIndexes'],
$changes['alteredFields'], $changes['alteredIndexes']); $changes['alteredFields'], $changes['alteredIndexes'], $changes['alteredOptions']);
break; break;
} }
} }
@ -191,9 +195,23 @@ abstract class Database extends Object {
// Transactional schema altering functions - they don't do anyhting except for update schemaUpdateTransaction // 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) { function transCreateField($table, $field, $schema) {
$this->transInitTable($table); $this->transInitTable($table);
$this->schemaUpdateTransaction[$table]['newFields'][$field] = $schema; $this->schemaUpdateTransaction[$table]['newFields'][$field] = $schema;
@ -223,6 +241,7 @@ abstract class Database extends Object {
'newIndexes' => array(), 'newIndexes' => array(),
'alteredFields' => array(), 'alteredFields' => array(),
'alteredIndexes' => array(), 'alteredIndexes' => array(),
'alteredOptions' => ''
); );
} }
} }
@ -231,16 +250,36 @@ abstract class Database extends Object {
/** /**
* Generate the following table in the database, modifying whatever already exists * Generate the following table in the database, modifying whatever already exists
* as necessary. * as necessary.
* @todo Change detection for CREATE TABLE $options other than "Engine"
*
* @param string $table The name of the table * @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 $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 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)])) { if(!isset($this->tableList[strtolower($table)])) {
$this->transCreateTable($table); $this->transCreateTable($table, $options);
Database::alteration_message("Table $table: created","created"); Database::alteration_message("Table $table: created","created");
} else { } 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: //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->setTable($table);
$fieldObj->requireField(); $fieldObj->requireField();
} }
} }
// Create custom indexes // Create custom indexes
if($indexSchema) { if($indexSchema) {

View File

@ -190,18 +190,25 @@ class MySQLDatabase extends Database {
return $this->query("SHOW DATABASES")->column(); 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 = ""; $fieldSchemas = $indexSchemas = "";
$addOptions = (isset($options[$this->class])) ? $options[$this->class] : null;
if(!isset($fields['ID'])) $fields['ID'] = "int(11) not null auto_increment"; if(!isset($fields['ID'])) $fields['ID'] = "int(11) not null auto_increment";
if($fields) foreach($fields as $k => $v) $fieldSchemas .= "\"$k\" $v,\n"; if($fields) foreach($fields as $k => $v) $fieldSchemas .= "\"$k\" $v,\n";
if($indexes) foreach($indexes as $k => $v) $indexSchemas .= $this->getIndexSqlDefinition($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 $fieldSchemas
$indexSchemas $indexSchemas
primary key (ID) 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 $newIndexes New indexes, a map of index name => index type
* @param $alteredFields Updated fields, a map of field name => field schema * @param $alteredFields Updated fields, a map of field name => field schema
* @param $alteredIndexes Updated indexes, a map of index name => index type * @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 = ""; $fieldSchemas = $indexSchemas = "";
$alterList = array();
if($newFields) foreach($newFields as $k => $v) $alterList[] .= "ADD \"$k\" $v"; if($newFields) foreach($newFields as $k => $v) $alterList[] .= "ADD \"$k\" $v";
if($newIndexes) foreach($newIndexes as $k => $v) $alterList[] .= "ADD " . $this->getIndexSqlDefinition($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); $alterations = implode(",\n", $alterList);
$this->query("ALTER TABLE \"$tableName\" $alterations"); $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) { public function renameTable($oldTableName, $newTableName) {

View File

@ -45,9 +45,25 @@ class DatabaseTest extends SapphireTest {
self::create_temp_db(); 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 { class DatabaseTest_MyObject extends DataObject implements TestOnly {
static $create_table_options = array('MySQLDatabase' => 'ENGINE=InnoDB');
static $db = array( static $db = array(
'MyField' => 'Varchar' 'MyField' => 'Varchar'
); );