mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Dramatically improved performance of db/build
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@40035 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
65c7a3f0f4
commit
9b1487c376
@ -227,5 +227,57 @@ class ManifestBuilder {
|
||||
require_once($filename);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the active table list in the class info in the manifest, but leaves everything else as-is.
|
||||
* Much quicker to run than compileManifest :-)
|
||||
*/
|
||||
static function update_db_tables() {
|
||||
global $_ALL_CLASSES;
|
||||
$_ALL_CLASSES['hastable'] = array();
|
||||
|
||||
$tables = DB::getConn()->tableList();
|
||||
|
||||
// We need to iterate through the full class lists, because the table names come out in lowercase
|
||||
foreach($_ALL_CLASSES['exists'] as $class) {
|
||||
if(isset($tables[strtolower($class)])) $_ALL_CLASSES['hastable'][$class] = $class;
|
||||
}
|
||||
|
||||
self::write_manifest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the manifest file, containing the updated values in the applicable globals
|
||||
*/
|
||||
static function write_manifest() {
|
||||
global $_CLASS_MANIFEST, $_TEMPLATE_MANIFEST, $_CSS_MANIFEST, $_ALL_CLASSES;
|
||||
|
||||
$manifest = "\$_CLASS_MANIFEST = " . var_export($_CLASS_MANIFEST, true) . ";\n";
|
||||
|
||||
// Config manifest
|
||||
$baseDir = dirname($_SERVER['SCRIPT_FILENAME']) . "/..";
|
||||
$baseDir = ereg_replace("/[^/]+/\\.\\.","",$baseDir);
|
||||
$topLevel = scandir($baseDir);
|
||||
|
||||
foreach($topLevel as $filename) {
|
||||
if(is_dir("$baseDir/$filename/") && file_exists("$baseDir/$filename/_config.php")) {
|
||||
$manifest .= "require_once(\"$baseDir/$filename/_config.php\");\n";
|
||||
}
|
||||
}
|
||||
|
||||
$manifest .= "\$_TEMPLATE_MANIFEST = " . var_export($_TEMPLATE_MANIFEST, true) . ";\n";
|
||||
$manifest .= "\$_CSS_MANIFEST = " . var_export($_CSS_MANIFEST, true) . ";\n";
|
||||
$manifest .= "\$_ALL_CLASSES = " . var_export($_ALL_CLASSES, true) . ";\n";
|
||||
$manifest = "<?php\n$manifest\n?>";
|
||||
|
||||
if($fh = fopen(MANIFEST_FILE,"w")) {
|
||||
fwrite($fh, $manifest);
|
||||
fclose($fh);
|
||||
|
||||
} else {
|
||||
die("Cannot write manifest file! Check permissions of " . MANIFEST_FILE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
@ -58,7 +58,12 @@ abstract class Database extends Object {
|
||||
* The table will have a single field - the integer key ID.
|
||||
* @param string $table Name of table to create.
|
||||
*/
|
||||
abstract function createTable($table);
|
||||
abstract function createTable($table, $fields = null, $indexes = null);
|
||||
|
||||
/**
|
||||
* Alter a table's schema.
|
||||
*/
|
||||
abstract function alterTable($table, $newFields, $newIndexes, $alteredFields, $alteredIndexes);
|
||||
|
||||
/**
|
||||
* Rename a table.
|
||||
@ -112,6 +117,59 @@ abstract class Database extends Object {
|
||||
*/
|
||||
protected $indexList;
|
||||
|
||||
|
||||
/**
|
||||
* Large array structure that represents a schema update transaction
|
||||
*/
|
||||
protected $schemaUpdateTransaction;
|
||||
|
||||
/**
|
||||
* Start a schema-updating transaction.
|
||||
* All calls to requireTable/Field/Index will keep track of the changes requested, but not actually do anything.
|
||||
* Once
|
||||
*/
|
||||
function beginSchemaUpdate() {
|
||||
$this->tableList = $this->tableList();
|
||||
$this->indexList = null;
|
||||
$this->fieldList = null;
|
||||
$this->schemaUpdateTransaction = array();
|
||||
}
|
||||
|
||||
function endSchemaUpdate() {
|
||||
foreach($this->schemaUpdateTransaction as $tableName => $changes) {
|
||||
switch($changes['command']) {
|
||||
case 'create':
|
||||
$this->createTable($tableName, $changes['newFields'], $changes['newIndexes']);
|
||||
break;
|
||||
|
||||
case 'alter':
|
||||
$this->alterTable($tableName, $changes['newFields'], $changes['newIndexes'],
|
||||
$changes['alteredFields'], $changes['alteredIndexes']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->schemaUpdateTransaction = null;
|
||||
}
|
||||
|
||||
// Transactional schema altering functions - they don't do anyhting except for update schemaUpdateTransaction
|
||||
|
||||
function transCreateTable($table) {
|
||||
$this->schemaUpdateTransaction[$table] = array('command' => 'create');
|
||||
}
|
||||
function transCreateField($table, $field, $schema) {
|
||||
$this->schemaUpdateTransaction[$table]['newFields'][$field] = $schema;
|
||||
}
|
||||
function transCreateIndex($table, $index, $schema) {
|
||||
$this->schemaUpdateTransaction[$table]['newIndexes'][$index] = $schema;
|
||||
}
|
||||
function transAlterField($table, $field, $schema) {
|
||||
$this->schemaUpdateTransaction[$table]['alteredFields'][$field] = $schema;
|
||||
}
|
||||
function transAlterIndex($table, $index, $schema) {
|
||||
$this->schemaUpdateTransaction[$table]['alteredIndexes'][$index] = $schema;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the following table in the database, modifying whatever already exists
|
||||
* as necessary.
|
||||
@ -124,12 +182,8 @@ abstract class Database extends Object {
|
||||
* control over the index.
|
||||
*/
|
||||
function requireTable($table, $fieldSchema = null, $indexSchema = null) {
|
||||
if(!isset($this->tableList)) {
|
||||
$this->tableList = $this->tableList();
|
||||
}
|
||||
|
||||
if(!isset($this->tableList[strtolower($table)])) {
|
||||
$this->createTable($table);
|
||||
$this->transCreateTable($table);
|
||||
if(!Database::$supressOutput) {
|
||||
echo "<li style=\"color: orange\">Table $table: created</li>";
|
||||
}
|
||||
@ -137,16 +191,10 @@ abstract class Database extends Object {
|
||||
$this->checkAndRepairTable($table);
|
||||
}
|
||||
|
||||
|
||||
// Create custom fields
|
||||
if($fieldSchema) {
|
||||
foreach($fieldSchema as $fieldName => $fieldSpec) {
|
||||
// echo "<li>$fieldName - " .ViewableData::castingObjectCreator($fieldSpec);
|
||||
|
||||
// Debug::show(ViewableData::castingObjectCreator($fieldSpec));
|
||||
|
||||
$fieldObj = eval(ViewableData::castingObjectCreator($fieldSpec));
|
||||
|
||||
$fieldObj->setTable($table);
|
||||
$fieldObj->requireField();
|
||||
}
|
||||
@ -189,16 +237,18 @@ abstract class Database extends Object {
|
||||
}
|
||||
$spec = ereg_replace(" *, *",",",$spec);
|
||||
|
||||
if(!isset($this->indexList[$table])) {
|
||||
if(!isset($this->tableList[strtolower($table)])) $newTable = true;
|
||||
|
||||
if(!$newTable && !isset($this->indexList[$table])) {
|
||||
$this->indexList[$table] = $this->indexList($table);
|
||||
}
|
||||
if(!isset($this->indexList[$table][$index])) {
|
||||
$this->createIndex($table, $index, $spec);
|
||||
if($newTable || !isset($this->indexList[$table][$index])) {
|
||||
$this->transCreateIndex($table, $index, $spec);
|
||||
if(!Database::$supressOutput) {
|
||||
echo "<li style=\"color: red\">Index $table.$index: created as $spec</li>";
|
||||
}
|
||||
} else if($this->indexList[$table][$index] != $spec) {
|
||||
$this->alterIndex($table, $index, $spec);
|
||||
$this->transAlterIndex($table, $index, $spec);
|
||||
if(!Database::$supressOutput) {
|
||||
echo "<li style=\"color: orange\">Index $table.$index: changed to $spec <i style=\"color: #AAA\">(from {$this->indexList[$table][$index]})</i></li>";
|
||||
}
|
||||
@ -212,27 +262,34 @@ abstract class Database extends Object {
|
||||
* @param string $spec The field specification.
|
||||
*/
|
||||
function requireField($table, $field, $spec) {
|
||||
Profiler::mark('requireField');
|
||||
// Collations didn't come in until MySQL 4.1. Anything earlier will throw a syntax error if you try and use
|
||||
// collations.
|
||||
if(!$this->supportsCollations()) {
|
||||
$spec = eregi_replace(' *character set [^ ]+( collate [^ ]+)?( |$)','\\2',$spec);
|
||||
}
|
||||
if(!isset($this->tableList[strtolower($table)])) $newTable = true;
|
||||
|
||||
if(!isset($this->fieldList[$table])) {
|
||||
if(!$newTable && !isset($this->fieldList[$table])) {
|
||||
$this->fieldList[$table] = $this->fieldList($table);
|
||||
}
|
||||
|
||||
if(!isset($this->fieldList[$table][$field])) {
|
||||
$this->createField($table, $field, $spec);
|
||||
if($newTable || !isset($this->fieldList[$table][$field])) {
|
||||
Profiler::mark('createField');
|
||||
$this->transCreateField($table, $field, $spec);
|
||||
Profiler::unmark('createField');
|
||||
if(!Database::$supressOutput) {
|
||||
echo "<li style=\"color: red\">Field $table.$field: created as $spec</li>";
|
||||
}
|
||||
} else if($this->fieldList[$table][$field] != $spec) {
|
||||
$this->alterField($table, $field, $spec);
|
||||
Profiler::mark('alterField');
|
||||
$this->transAlterField($table, $field, $spec);
|
||||
Profiler::unmark('alterField');
|
||||
if(!Database::$supressOutput) {
|
||||
echo "<li style=\"color: orange\">Field $table.$field: changed to $spec <i style=\"color: #AAA\">(from {$this->fieldList[$table][$field]})</i></li>";
|
||||
}
|
||||
}
|
||||
Profiler::unmark('requireField');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -121,6 +121,9 @@ class DatabaseAdmin extends Controller {
|
||||
* @param boolean $quiet Don't show messages
|
||||
*/
|
||||
function doBuild($quiet = false) {
|
||||
$conn = DB::getConn();
|
||||
|
||||
Profiler::mark('doBuild');
|
||||
if($quiet) {
|
||||
DB::quiet();
|
||||
} else {
|
||||
@ -136,8 +139,8 @@ class DatabaseAdmin extends Controller {
|
||||
}
|
||||
|
||||
// Get all our classes
|
||||
ManifestBuilder::compileManifest();
|
||||
ManifestBuilder::includeEverything();
|
||||
// ManifestBuilder::compileManifest();
|
||||
// ManifestBuilder::includeEverything();
|
||||
|
||||
// Build the database. Most of the hard work is handled by DataObject
|
||||
$dataClasses = ClassInfo::subclassesFor('DataObject');
|
||||
@ -147,6 +150,7 @@ class DatabaseAdmin extends Controller {
|
||||
echo '<p><b>Creating database tables</b></p>';
|
||||
}
|
||||
|
||||
$conn->beginSchemaUpdate();
|
||||
foreach($dataClasses as $dataClass) {
|
||||
// Test_ indicates that it's the data class is part of testing system
|
||||
|
||||
@ -154,12 +158,14 @@ class DatabaseAdmin extends Controller {
|
||||
if(!$quiet) {
|
||||
echo "<li>$dataClass";
|
||||
}
|
||||
|
||||
Profiler::mark("requireTable $dataClass");
|
||||
singleton($dataClass)->requireTable();
|
||||
Profiler::unmark("requireTable $dataClass");
|
||||
}
|
||||
}
|
||||
$conn->endSchemaUpdate();
|
||||
|
||||
ManifestBuilder::compileManifest();
|
||||
ManifestBuilder::update_db_tables();
|
||||
|
||||
if(!$quiet) {
|
||||
echo '<p><b>Creating database records</b></p>';
|
||||
@ -173,7 +179,9 @@ class DatabaseAdmin extends Controller {
|
||||
echo "<li>$dataClass";
|
||||
}
|
||||
|
||||
Profiler::mark("requireDefaultRecords $dataClass");
|
||||
singleton($dataClass)->requireDefaultRecords();
|
||||
Profiler::unmark("requireDefaultRecords $dataClass");
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,6 +190,7 @@ class DatabaseAdmin extends Controller {
|
||||
if(isset($_REQUEST['from_installer'])) {
|
||||
echo "OK";
|
||||
}
|
||||
Profiler::unmark('doBuild');
|
||||
}
|
||||
|
||||
|
||||
|
@ -114,20 +114,91 @@ class MySQLDatabase extends Database {
|
||||
|
||||
public function createDatabase() {
|
||||
$this->query("CREATE DATABASE $this->database");
|
||||
$this->query("USE $this->database");
|
||||
|
||||
$this->tableList = $this->fieldList = $this->indexList = null;
|
||||
|
||||
if(mysql_select_db($this->database, $this->dbConn)) {
|
||||
$this->active = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function createTable($tableName) {
|
||||
$this->query("CREATE TABLE `$tableName` (ID int(11) not null auto_increment, primary key (ID)) TYPE=MyISAM");
|
||||
/**
|
||||
* Drop the database that this object is currently connected to.
|
||||
* Use with caution.
|
||||
*/
|
||||
public function dropDatabase() {
|
||||
$this->query("DROP DATABASE $this->database");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the currently selected database
|
||||
*/
|
||||
public function currentDatabase() {
|
||||
return $this->database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches to the given database.
|
||||
* If the database doesn't exist, you should call createDatabase() after calling selectDatabase()
|
||||
*/
|
||||
public function selectDatabase($dbname) {
|
||||
$this->database = $dbname;
|
||||
if($this->databaseExists($this->databse)) mysql_select_db($this->database, $this->dbConn);
|
||||
$this->tableList = $this->fieldList = $this->indexList = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the named database exists.
|
||||
*/
|
||||
public function databaseExists($name) {
|
||||
$SQL_name = Convert::raw2sql($name);
|
||||
return $this->query("SHOW DATABASES LIKE '$SQL_name'")->value() ? true : false;
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
$this->query("CREATE TABLE `$tableName` (
|
||||
ID int(11) not null auto_increment,
|
||||
$fieldSchemas
|
||||
$indexSchemas
|
||||
primary key (ID)
|
||||
) TYPE=MyISAM");
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter a table's schema.
|
||||
* @param $table The name of the table to alter
|
||||
* @param $newFields New fields, a map of field name => field schema
|
||||
* @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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
public function renameTable($oldTableName, $newTableName) {
|
||||
$this->query("ALTER TABLE `$oldTableName` RENAME `$newTableName`");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Checks a table's integrity and repairs it if necessary.
|
||||
* @var string $tableName The name of the table.
|
||||
@ -220,14 +291,17 @@ class MySQLDatabase extends Database {
|
||||
* @param string $indexSpec The specification of the index, see Database::requireIndex() for more details.
|
||||
*/
|
||||
public function createIndex($tableName, $indexName, $indexSpec) {
|
||||
$this->query("ALTER TABLE `$tableName` ADD " . $this->getIndexSqlDefinition($indexName, $indexSpec));
|
||||
}
|
||||
|
||||
protected function getIndexSqlDefinition($indexName, $indexSpec) {
|
||||
$indexSpec = trim($indexSpec);
|
||||
if($indexSpec[0] != '(') list($indexType, $indexFields) = explode(' ',$indexSpec,2);
|
||||
else $indexFields = $indexSpec;
|
||||
if(!isset($indexType)) {
|
||||
$indexType = "index";
|
||||
}
|
||||
|
||||
$this->query("ALTER TABLE `$tableName` ADD $indexType `$indexName` $indexFields");
|
||||
return "$indexType `$indexName` $indexFields";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -280,7 +354,6 @@ class MySQLDatabase extends Database {
|
||||
return $indexList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a list of all the tables in the database.
|
||||
* Table names will all be in lowercase.
|
||||
|
@ -395,14 +395,19 @@ class SiteTree extends DataObject {
|
||||
function requireDefaultRecords() {
|
||||
parent::requireDefaultRecords();
|
||||
|
||||
if($this->class == 'SiteTree') {
|
||||
if(!DataObject::get_one("SiteTree", "URLSegment = 'home'")) {
|
||||
$homepage = new Page();
|
||||
echo 'Running with the homepage: ' . $homepage->ID;
|
||||
|
||||
$homepage->Title = "Home";
|
||||
$homepage->Content = "<p>Welcome to SilverStripe! This is the default homepage. You can edit this page by opening <a href=\"admin/\">the CMS</a>.</p>";
|
||||
$homepage->URLSegment = "home";
|
||||
$homepage->Status = "Published";
|
||||
$homepage->write();
|
||||
echo 'Created the homepage: ' . $homepage->ID;
|
||||
$homepage->publish("Stage", "Live");
|
||||
$homepage->flushCache();
|
||||
|
||||
if(!Database::$supressOutput) {
|
||||
echo "<li style=\"color: orange\">Home page created</li>";
|
||||
@ -429,9 +434,10 @@ class SiteTree extends DataObject {
|
||||
$contactus->Status = "Published";
|
||||
$contactus->write();
|
||||
$contactus->publish("Stage", "Live");
|
||||
|
||||
$contactus->flushCache();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------//
|
||||
@ -698,6 +704,8 @@ class SiteTree extends DataObject {
|
||||
$fields = call_user_func($extension,$fields);
|
||||
}
|
||||
|
||||
$this->extend('updateCMSFields', $fields);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user