API CHANGE: Allow passing of temporary option to Database::createTable()

BUGFIX: Made Versioned's archive table functions (show deleted pages and show site at a particular date) db agnostic.

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@77551 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Sam Minnee 2009-05-21 05:08:11 +00:00
parent 90ebeaf741
commit 9effc74de3
4 changed files with 70 additions and 30 deletions

View File

@ -159,11 +159,16 @@ class DB {
/**
* Create a new table.
* The table will have a single field - the integer key ID.
* @param string $table Name of table to create.
* @param $tableName The name of the table
* @param $fields A map of field names to field types
* @param $indexes A map of indexes
* @param $options An map of additional options. The available keys are as follows:
* - 'MSSQLDatabase'/'MySQLDatabase'/'PostgreSQLDatabase' - database-specific options such as "engine" for MySQL.
* - 'temporary' - If true, then a temporary table will be created
* @return The table name generated. This may be different from the table name, for example with temporary tables.
*/
static function createTable($table) {
return DB::$globalConn->createTable($table);
static function createTable($table, $fields = null, $indexes = null, $options = null) {
return DB::$globalConn->createTable($table, $fields, $indexes, $options);
}
/**

View File

@ -70,12 +70,13 @@ abstract class Database extends Object {
/**
* Create a new table.
* The table will have a single field - the integer key ID.
* @param string $table
* @param array $fields
* @param array $indexes
* @param string $driver
* @param array $options
* @param $tableName The name of the table
* @param $fields A map of field names to field types
* @param $indexes A map of indexes
* @param $options An map of additional options. The available keys are as follows:
* - 'MSSQLDatabase'/'MySQLDatabase'/'PostgreSQLDatabase' - database-specific options such as "engine" for MySQL.
* - 'temporary' - If true, then a temporary table will be created
* @return The table name generated. This may be different from the table name, for example with temporary tables.
*/
abstract function createTable($table, $fields = null, $indexes = null, $options = null);

View File

@ -191,10 +191,14 @@ class MySQLDatabase extends Database {
}
/**
* @param string $table
* @param array $fields
* @param array $indexes
* @param string $options
* Create a new table.
* @param $tableName The name of the table
* @param $fields A map of field names to field types
* @param $indexes A map of indexes
* @param $options An map of additional options. The available keys are as follows:
* - 'MSSQLDatabase'/'MySQLDatabase'/'PostgreSQLDatabase' - database-specific options such as "engine" for MySQL.
* - 'temporary' - If true, then a temporary table will be created
* @return The table name generated. This may be different from the table name, for example with temporary tables.
*/
public function createTable($table, $fields = null, $indexes = null, $options = null) {
$fieldSchemas = $indexSchemas = "";
@ -204,11 +208,16 @@ class MySQLDatabase extends Database {
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 \"$table\" (
// Switch to "CREATE TEMPORARY TABLE" for temporary tables
$temporary = empty($options['temporary']) ? "" : "TEMPORARY";
$this->query("CREATE $temporary TABLE \"$table\" (
$fieldSchemas
$indexSchemas
primary key (ID)
) {$addOptions}");
return $table;
}
/**
@ -724,6 +733,22 @@ class MySQLDatabase extends Database {
function modifyIndex($index){
return $index;
}
/**
* Returns a SQL fragment for querying a fulltext search index
* @param $fields array The list of field names to search on
* @param $keywords string The search query
* @param $booleanSearch A MySQL-specific flag to switch to boolean search
*/
function fullTextSearchSQL($fields, $keywords, $booleanSearch = false) {
$boolean = $booleanSearch ? "IN BOOLEAN MODE" : "";
$fieldNames = '"' . implode('", "', $fields) . '"';
$SQL_keywords = Convert::raw2sql($keywords);
$SQL_htmlEntityKeywords = Convert::raw2sql(htmlentities($keywords));
return "(MATCH ($fieldNames) AGAINST ('$SQL_keywords' $boolean) + MATCH ($fieldNames) AGAINST ('$SQL_htmlEntityKeywords' $boolean))";
}
}
/**

View File

@ -119,10 +119,10 @@ class Versioned extends DataObjectDecorator {
}
// Link to the version archived on that date
$this->requireArchiveTempTable($baseTable, $date);
$query->from["_Archive$baseTable"] = "INNER JOIN \"_Archive$baseTable\"
ON \"_Archive$baseTable\".\"RecordID\" = \"{$baseTable}_versions\".\"RecordID\"
AND \"_Archive$baseTable\".\"Version\" = \"{$baseTable}_versions\".\"Version\"";
$archiveTable = $this->requireArchiveTempTable($baseTable, $date);
$query->from[$archiveTable] = "INNER JOIN \"$archiveTable\"
ON \"$archiveTable\".\"ID\" = \"{$baseTable}_versions\".\"RecordID\"
AND \"$archiveTable\".\"Version\" = \"{$baseTable}_versions\".\"Version\"";
// Get a specific stage
} else if(Versioned::$reading_stage && Versioned::$reading_stage != $this->defaultStage
@ -133,6 +133,11 @@ class Versioned extends DataObjectDecorator {
}
}
/**
* Keep track of the archive tables that have been created
*/
private static $archive_tables = array();
/**
* Create a temporary table mapping each database record to its version on the given date.
* This is used by the versioning system to return database content on that date.
@ -141,20 +146,24 @@ class Versioned extends DataObjectDecorator {
* @todo Ensure that this is DB abstracted
*/
protected static function requireArchiveTempTable($baseTable, $date = null) {
DB::query("CREATE TEMPORARY TABLE IF NOT EXISTS \"_Archive$baseTable\" (
\"RecordID\" INT NOT NULL PRIMARY KEY,
\"Version\" INT NOT NULL
)");
if(!DB::query("SELECT COUNT(*) FROM \"_Archive$baseTable\"")->value()) {
if(!isset(self::$archive_tables[$baseTable])) {
self::$archive_tables[$baseTable] = DB::createTable("_Archive$baseTable", array(
"ID" => "INT NOT NULL",
"Version" => "INT NOT NULL",
), null, array('temporary' => true));
}
if(!DB::query("SELECT COUNT(*) FROM \"" . self::$archive_tables[$baseTable] . "\"")->value()) {
if($date) $dateClause = "WHERE \"LastEdited\" <= '$date'";
else $dateClause = "";
DB::query("INSERT INTO \"_Archive$baseTable\"
DB::query("INSERT INTO \"" . self::$archive_tables[$baseTable] . "\"
SELECT \"RecordID\", max(\"Version\") FROM \"{$baseTable}_versions\"
$dateClause
GROUP BY \"RecordID\"");
}
return self::$archive_tables[$baseTable];
}
/**
@ -781,10 +790,10 @@ class Versioned extends DataObjectDecorator {
// Build query
$query = $SNG->buildVersionSQL($filter, $sort);
$baseTable = ClassInfo::baseDataClass($class);
self::requireArchiveTempTable($baseTable);
$query->from["_Archive$baseTable"] = "INNER JOIN `_Archive$baseTable`
ON `_Archive$baseTable`.RecordID = `{$baseTable}_versions`.RecordID
AND `_Archive$baseTable`.Version = `{$baseTable}_versions`.Version";
$archiveTable = self::requireArchiveTempTable($baseTable);
$query->from[$archiveTable] = "INNER JOIN \"$archiveTable\"
ON \"$archiveTable\".\"ID\" = \"{$baseTable}_versions\".\"RecordID\"
AND \"$archiveTable\".\"Version\" = \"{$baseTable}_versions\".\"Version\"";
// Process into a DataObjectSet
$result = $SNG->buildDataObjectSet($query->execute());