From 9effc74de386a6e25cc947a6f0f63b493a3da756 Mon Sep 17 00:00:00 2001 From: Sam Minnee Date: Thu, 21 May 2009 05:08:11 +0000 Subject: [PATCH] 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 --- core/model/DB.php | 13 ++++++++---- core/model/Database.php | 13 ++++++------ core/model/MySQLDatabase.php | 35 +++++++++++++++++++++++++++----- core/model/Versioned.php | 39 ++++++++++++++++++++++-------------- 4 files changed, 70 insertions(+), 30 deletions(-) diff --git a/core/model/DB.php b/core/model/DB.php index 7f2adeef6..d7ee21609 100755 --- a/core/model/DB.php +++ b/core/model/DB.php @@ -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); } /** diff --git a/core/model/Database.php b/core/model/Database.php index b8e8d2a9e..e8190f9a2 100755 --- a/core/model/Database.php +++ b/core/model/Database.php @@ -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); diff --git a/core/model/MySQLDatabase.php b/core/model/MySQLDatabase.php index 358b80700..361f890cb 100644 --- a/core/model/MySQLDatabase.php +++ b/core/model/MySQLDatabase.php @@ -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))"; + } } /** diff --git a/core/model/Versioned.php b/core/model/Versioned.php index 15853d147..320d2c7af 100755 --- a/core/model/Versioned.php +++ b/core/model/Versioned.php @@ -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());