/**
  * Check the database schema and update it as necessary.
  *
  * @uses DataExtension->augmentDatabase()
  */
 public function requireTable()
 {
     // Only build the table if we've actually got fields
     $fields = self::database_fields($this->class);
     $extensions = self::database_extensions($this->class);
     $indexes = $this->databaseIndexes();
     if ($fields) {
         $hasAutoIncPK = $this->class == ClassInfo::baseDataClass($this->class);
         DB::require_table($this->class, $fields, $indexes, $hasAutoIncPK, $this->stat('create_table_options'), $extensions);
     } else {
         DB::dont_require_table($this->class);
     }
     // Build any child tables for many_many items
     if ($manyMany = $this->uninherited('many_many', true)) {
         $extras = $this->uninherited('many_many_extraFields', true);
         foreach ($manyMany as $relationship => $childClass) {
             // Build field list
             $manymanyFields = array("{$this->class}ID" => "Int", $this->class == $childClass ? "ChildID" : "{$childClass}ID" => "Int");
             if (isset($extras[$relationship])) {
                 $manymanyFields = array_merge($manymanyFields, $extras[$relationship]);
             }
             // Build index list
             $manymanyIndexes = array("{$this->class}ID" => true, $this->class == $childClass ? "ChildID" : "{$childClass}ID" => true);
             DB::require_table("{$this->class}_{$relationship}", $manymanyFields, $manymanyIndexes, true, null, $extensions);
         }
     }
     // Let any extentions make their own database fields
     $this->extend('augmentDatabase', $dummy);
 }
 public function augmentDatabase()
 {
     $classTable = $this->owner->class;
     $isRootClass = $this->owner->class == ClassInfo::baseDataClass($this->owner->class);
     // Build a list of suffixes whose tables need versioning
     $allSuffixes = array();
     foreach (Versioned::$versionableExtensions as $versionableExtension => $suffixes) {
         if ($this->owner->hasExtension($versionableExtension)) {
             $allSuffixes = array_merge($allSuffixes, (array) $suffixes);
             foreach ((array) $suffixes as $suffix) {
                 $allSuffixes[$suffix] = $versionableExtension;
             }
         }
     }
     // Add the default table with an empty suffix to the list (table name = class name)
     array_push($allSuffixes, '');
     foreach ($allSuffixes as $key => $suffix) {
         // check that this is a valid suffix
         if (!is_int($key)) {
             continue;
         }
         if ($suffix) {
             $table = "{$classTable}_{$suffix}";
         } else {
             $table = $classTable;
         }
         if ($fields = DataObject::database_fields($this->owner->class)) {
             $options = Config::inst()->get($this->owner->class, 'create_table_options', Config::FIRST_SET);
             $indexes = $this->owner->databaseIndexes();
             if ($suffix && ($ext = $this->owner->getExtensionInstance($allSuffixes[$suffix]))) {
                 if (!$ext->isVersionedTable($table)) {
                     continue;
                 }
                 $ext->setOwner($this->owner);
                 $fields = $ext->fieldsInExtraTables($suffix);
                 $ext->clearOwner();
                 $indexes = $fields['indexes'];
                 $fields = $fields['db'];
             }
             // Create tables for other stages
             foreach ($this->stages as $stage) {
                 // Extra tables for _Live, etc.
                 // Change unique indexes to 'index'.  Versioned tables may run into unique indexing difficulties
                 // otherwise.
                 $indexes = $this->uniqueToIndex($indexes);
                 if ($stage != $this->defaultStage) {
                     DB::require_table("{$table}_{$stage}", $fields, $indexes, false, $options);
                 }
                 // Version fields on each root table (including Stage)
                 /*
                 if($isRootClass) {
                 	$stageTable = ($stage == $this->defaultStage) ? $table : "{$table}_$stage";
                 	$parts=Array('datatype'=>'int', 'precision'=>11, 'null'=>'not null', 'default'=>(int)0);
                 	$values=Array('type'=>'int', 'parts'=>$parts);
                 	DB::requireField($stageTable, 'Version', $values);
                 }
                 */
             }
             if ($isRootClass) {
                 // Create table for all versions
                 $versionFields = array_merge(Config::inst()->get('Versioned', 'db_for_versions_table'), (array) $fields);
                 $versionIndexes = array_merge(Config::inst()->get('Versioned', 'indexes_for_versions_table'), (array) $indexes);
             } else {
                 // Create fields for any tables of subclasses
                 $versionFields = array_merge(array("RecordID" => "Int", "Version" => "Int"), (array) $fields);
                 //Unique indexes will not work on versioned tables, so we'll convert them to standard indexes:
                 $indexes = $this->uniqueToIndex($indexes);
                 $versionIndexes = array_merge(array('RecordID_Version' => array('type' => 'unique', 'value' => '"RecordID","Version"'), 'RecordID' => true, 'Version' => true), (array) $indexes);
             }
             if (DB::get_schema()->hasTable("{$table}_versions")) {
                 // Fix data that lacks the uniqueness constraint (since this was added later and
                 // bugs meant that the constraint was validated)
                 $duplications = DB::query("SELECT MIN(\"ID\") AS \"ID\", \"RecordID\", \"Version\"\n\t\t\t\t\t\tFROM \"{$table}_versions\" GROUP BY \"RecordID\", \"Version\"\n\t\t\t\t\t\tHAVING COUNT(*) > 1");
                 foreach ($duplications as $dup) {
                     DB::alteration_message("Removing {$table}_versions duplicate data for " . "{$dup['RecordID']}/{$dup['Version']}", "deleted");
                     DB::prepared_query("DELETE FROM \"{$table}_versions\" WHERE \"RecordID\" = ?\n\t\t\t\t\t\t\tAND \"Version\" = ? AND \"ID\" != ?", array($dup['RecordID'], $dup['Version'], $dup['ID']));
                 }
                 // Remove junk which has no data in parent classes. Only needs to run the following
                 // when versioned data is spread over multiple tables
                 if (!$isRootClass && ($versionedTables = ClassInfo::dataClassesFor($table))) {
                     foreach ($versionedTables as $child) {
                         if ($table == $child) {
                             break;
                         }
                         // only need subclasses
                         $count = DB::query("\n\t\t\t\t\t\t\t\tSELECT COUNT(*) FROM \"{$table}_versions\"\n\t\t\t\t\t\t\t\tLEFT JOIN \"{$child}_versions\"\n\t\t\t\t\t\t\t\t\tON \"{$child}_versions\".\"RecordID\" = \"{$table}_versions\".\"RecordID\"\n\t\t\t\t\t\t\t\t\tAND \"{$child}_versions\".\"Version\" = \"{$table}_versions\".\"Version\"\n\t\t\t\t\t\t\t\tWHERE \"{$child}_versions\".\"ID\" IS NULL\n\t\t\t\t\t\t\t")->value();
                         if ($count > 0) {
                             DB::alteration_message("Removing orphaned versioned records", "deleted");
                             $affectedIDs = DB::query("\n\t\t\t\t\t\t\t\t\tSELECT \"{$table}_versions\".\"ID\" FROM \"{$table}_versions\"\n\t\t\t\t\t\t\t\t\tLEFT JOIN \"{$child}_versions\"\n\t\t\t\t\t\t\t\t\t\tON \"{$child}_versions\".\"RecordID\" = \"{$table}_versions\".\"RecordID\"\n\t\t\t\t\t\t\t\t\t\tAND \"{$child}_versions\".\"Version\" = \"{$table}_versions\".\"Version\"\n\t\t\t\t\t\t\t\t\tWHERE \"{$child}_versions\".\"ID\" IS NULL\n\t\t\t\t\t\t\t\t")->column();
                             if (is_array($affectedIDs)) {
                                 foreach ($affectedIDs as $key => $value) {
                                     DB::prepared_query("DELETE FROM \"{$table}_versions\" WHERE \"ID\" = ?", array($value));
                                 }
                             }
                         }
                     }
                 }
             }
             DB::require_table("{$table}_versions", $versionFields, $versionIndexes, true, $options);
         } else {
             DB::dont_require_table("{$table}_versions");
             foreach ($this->stages as $stage) {
                 if ($stage != $this->defaultStage) {
                     DB::dont_require_table("{$table}_{$stage}");
                 }
             }
         }
     }
 }