/**
  * Perform migration
  *
  * @param string $base Absolute base path (parent of assets folder). Will default to BASE_PATH
  * @return int Number of files successfully migrated
  */
 public function run($base = null)
 {
     if (empty($base)) {
         $base = BASE_PATH;
     }
     // Check if the File dataobject has a "Filename" field.
     // If not, cannot migrate
     /** @skipUpgrade */
     if (!DB::get_schema()->hasField('File', 'Filename')) {
         return 0;
     }
     // Set max time and memory limit
     increase_time_limit_to();
     increase_memory_limit_to();
     // Loop over all files
     $count = 0;
     $originalState = Versioned::get_reading_mode();
     Versioned::set_stage(Versioned::DRAFT);
     $filenameMap = $this->getFilenameArray();
     foreach ($this->getFileQuery() as $file) {
         // Get the name of the file to import
         $filename = $filenameMap[$file->ID];
         $success = $this->migrateFile($base, $file, $filename);
         if ($success) {
             $count++;
         }
     }
     Versioned::set_reading_mode($originalState);
     return $count;
 }
Ejemplo n.º 2
0
 /**
  * Returns the manifest of all classes which are present in the database.
  *
  * @param string $class Class name to check enum values for ClassName field
  * @param boolean $includeUnbacked Flag indicating whether or not to include
  * types that don't exist as implemented classes. By default these are excluded.
  * @return array List of subclasses
  */
 public static function getValidSubClasses($class = 'SilverStripe\\CMS\\Model\\SiteTree', $includeUnbacked = false)
 {
     if (is_string($class) && !class_exists($class)) {
         return array();
     }
     $class = self::class_name($class);
     if ($includeUnbacked) {
         $table = DataObject::getSchema()->tableName($class);
         $classes = DB::get_schema()->enumValuesForField($table, 'ClassName');
     } else {
         $classes = static::subclassesFor($class);
     }
     return $classes;
 }
 public function testUniqueIndexes()
 {
     $tableExpectations = array('VersionedTest_WithIndexes' => array('value' => true, 'message' => 'Unique indexes are unique in main table'), 'VersionedTest_WithIndexes_versions' => array('value' => false, 'message' => 'Unique indexes are no longer unique in _versions table'), 'VersionedTest_WithIndexes_Live' => array('value' => true, 'message' => 'Unique indexes are unique in _Live table'));
     // Test each table's performance
     foreach ($tableExpectations as $tableName => $expectation) {
         $indexes = DB::get_schema()->indexList($tableName);
         // Check for presence of all unique indexes
         $indexColumns = array_map(function ($index) {
             return $index['value'];
         }, $indexes);
         sort($indexColumns);
         $expectedColumns = array('"UniqA"', '"UniqS"');
         $this->assertEquals(array_values($expectedColumns), array_values(array_intersect($indexColumns, $expectedColumns)), "{$tableName} has both indexes");
         // Check unique -> non-unique conversion
         foreach ($indexes as $indexKey => $indexSpec) {
             if (in_array($indexSpec['value'], $expectedColumns)) {
                 $isUnique = $indexSpec['type'] === 'unique';
                 $this->assertEquals($isUnique, $expectation['value'], $expectation['message']);
             }
         }
     }
 }
 /**
  * Get the list of classnames, including obsolete classes.
  *
  * If table or name are not set, or if it is not a valid field on the given table,
  * then only known classnames are returned.
  *
  * Values cached in this method can be cleared via `DBClassName::clear_classname_cache();`
  *
  * @return array
  */
 public function getEnumObsolete()
 {
     // Without a table or field specified, we can only retrieve known classes
     $table = $this->getTable();
     $name = $this->getName();
     if (empty($table) || empty($name)) {
         return $this->getEnum();
     }
     // Ensure the table level cache exists
     if (empty(self::$classname_cache[$table])) {
         self::$classname_cache[$table] = array();
     }
     // Check existing cache
     if (!empty(self::$classname_cache[$table][$name])) {
         return self::$classname_cache[$table][$name];
     }
     // Get all class names
     $classNames = $this->getEnum();
     if (DB::get_schema()->hasField($table, $name)) {
         $existing = DB::query("SELECT DISTINCT \"{$name}\" FROM \"{$table}\"")->column();
         $classNames = array_unique(array_merge($classNames, $existing));
     }
     // Cache and return
     self::$classname_cache[$table][$name] = $classNames;
     return $classNames;
 }
 public function testHasTable()
 {
     $this->assertTrue(DB::get_schema()->hasTable('DatabaseTest_MyObject'));
     $this->assertFalse(DB::get_schema()->hasTable('asdfasdfasdf'));
 }
 /**
  * Reset the testing database's schema.
  * @param $includeExtraDataObjects If true, the extraDataObjects tables will also be included
  */
 public function resetDBSchema($includeExtraDataObjects = false)
 {
     if (self::using_temp_db()) {
         DataObject::reset();
         // clear singletons, they're caching old extension info which is used in DatabaseAdmin->doBuild()
         Injector::inst()->unregisterAllObjects();
         $dataClasses = ClassInfo::subclassesFor('SilverStripe\\ORM\\DataObject');
         array_shift($dataClasses);
         DB::quiet();
         $schema = DB::get_schema();
         $extraDataObjects = $includeExtraDataObjects ? $this->extraDataObjects : null;
         $schema->schemaUpdate(function () use($dataClasses, $extraDataObjects) {
             foreach ($dataClasses as $dataClass) {
                 // Check if class exists before trying to instantiate - this sidesteps any manifest weirdness
                 if (class_exists($dataClass)) {
                     $SNG = singleton($dataClass);
                     if (!$SNG instanceof TestOnly) {
                         $SNG->requireTable();
                     }
                 }
             }
             // If we have additional dataobjects which need schema, do so here:
             if ($extraDataObjects) {
                 foreach ($extraDataObjects as $dataClass) {
                     $SNG = singleton($dataClass);
                     if (singleton($dataClass) instanceof DataObject) {
                         $SNG->requireTable();
                     }
                 }
             }
         });
         ClassInfo::reset_db_cache();
         singleton('SilverStripe\\ORM\\DataObject')->flushCache();
     }
 }
 /**
  * Updates the database schema, creating tables & fields as necessary.
  *
  * @param boolean $quiet Don't show messages
  * @param boolean $populate Populate the database, as well as setting up its schema
  * @param bool $testMode
  */
 public function doBuild($quiet = false, $populate = true, $testMode = false)
 {
     if ($quiet) {
         DB::quiet();
     } else {
         $conn = DB::get_conn();
         // Assumes database class is like "MySQLDatabase" or "MSSQLDatabase" (suffixed with "Database")
         $dbType = substr(get_class($conn), 0, -8);
         $dbVersion = $conn->getVersion();
         $databaseName = method_exists($conn, 'currentDatabase') ? $conn->getSelectedDatabase() : "";
         if (Director::is_cli()) {
             echo sprintf("\n\nBuilding database %s using %s %s\n\n", $databaseName, $dbType, $dbVersion);
         } else {
             echo sprintf("<h2>Building database %s using %s %s</h2>", $databaseName, $dbType, $dbVersion);
         }
     }
     // Set up the initial database
     if (!DB::is_active()) {
         if (!$quiet) {
             echo '<p><b>Creating database</b></p>';
         }
         // Load parameters from existing configuration
         global $databaseConfig;
         if (empty($databaseConfig) && empty($_REQUEST['db'])) {
             user_error("No database configuration available", E_USER_ERROR);
         }
         $parameters = !empty($databaseConfig) ? $databaseConfig : $_REQUEST['db'];
         // Check database name is given
         if (empty($parameters['database'])) {
             user_error("No database name given; please give a value for \$databaseConfig['database']", E_USER_ERROR);
         }
         $database = $parameters['database'];
         // Establish connection and create database in two steps
         unset($parameters['database']);
         DB::connect($parameters);
         DB::create_database($database);
     }
     // Build the database.  Most of the hard work is handled by DataObject
     $dataClasses = ClassInfo::subclassesFor('SilverStripe\\ORM\\DataObject');
     array_shift($dataClasses);
     if (!$quiet) {
         if (Director::is_cli()) {
             echo "\nCREATING DATABASE TABLES\n\n";
         } else {
             echo "\n<p><b>Creating database tables</b></p>\n\n";
         }
     }
     // Initiate schema update
     $dbSchema = DB::get_schema();
     $dbSchema->schemaUpdate(function () use($dataClasses, $testMode, $quiet) {
         foreach ($dataClasses as $dataClass) {
             // Check if class exists before trying to instantiate - this sidesteps any manifest weirdness
             if (!class_exists($dataClass)) {
                 continue;
             }
             // Check if this class should be excluded as per testing conventions
             $SNG = singleton($dataClass);
             if (!$testMode && $SNG instanceof TestOnly) {
                 continue;
             }
             // Log data
             if (!$quiet) {
                 if (Director::is_cli()) {
                     echo " * {$dataClass}\n";
                 } else {
                     echo "<li>{$dataClass}</li>\n";
                 }
             }
             // Instruct the class to apply its schema to the database
             $SNG->requireTable();
         }
     });
     ClassInfo::reset_db_cache();
     if ($populate) {
         if (!$quiet) {
             if (Director::is_cli()) {
                 echo "\nCREATING DATABASE RECORDS\n\n";
             } else {
                 echo "\n<p><b>Creating database records</b></p>\n\n";
             }
         }
         foreach ($dataClasses as $dataClass) {
             // Check if class exists before trying to instantiate - this sidesteps any manifest weirdness
             // Test_ indicates that it's the data class is part of testing system
             if (strpos($dataClass, 'Test_') === false && class_exists($dataClass)) {
                 if (!$quiet) {
                     if (Director::is_cli()) {
                         echo " * {$dataClass}\n";
                     } else {
                         echo "<li>{$dataClass}</li>\n";
                     }
                 }
                 singleton($dataClass)->requireDefaultRecords();
             }
         }
         // Remap obsolete class names
         $schema = DataObject::getSchema();
         foreach ($this->config()->classname_value_remapping as $oldClassName => $newClassName) {
             $badRecordCount = $newClassName::get()->filter(["ClassName" => $oldClassName])->count();
             if ($badRecordCount > 0) {
                 if (Director::is_cli()) {
                     echo " * Correcting {$badRecordCount} obsolete classname values for {$newClassName}\n";
                 } else {
                     echo "<li>Correcting {$badRecordCount} obsolete classname values for {$newClassName}</li>\n";
                 }
                 $table = $schema->baseDataTable($newClassName);
                 DB::prepared_query("UPDATE \"{$table}\" SET \"ClassName\" = ? WHERE \"ClassName\" = ?", [$newClassName, $oldClassName]);
             }
         }
     }
     touch(TEMP_FOLDER . '/database-last-generated-' . str_replace(array('\\', '/', ':'), '.', Director::baseFolder()));
     if (isset($_REQUEST['from_installer'])) {
         echo "OK";
     }
     if (!$quiet) {
         echo Director::is_cli() ? "\n Database build completed!\n\n" : "<p>Database build completed!</p>";
     }
     ClassInfo::reset_db_cache();
 }
 public function testDatabaseIsReadyWithInsufficientMemberColumns()
 {
     $old = Security::$force_database_is_ready;
     Security::$force_database_is_ready = null;
     Security::$database_is_ready = false;
     DBClassName::clear_classname_cache();
     // Assumption: The database has been built correctly by the test runner,
     // and has all columns present in the ORM
     /** @skipUpgrade */
     DB::get_schema()->renameField('Member', 'Email', 'Email_renamed');
     // Email column is now missing, which means we're not ready to do permission checks
     $this->assertFalse(Security::database_is_ready());
     // Rebuild the database (which re-adds the Email column), and try again
     $this->resetDBSchema(true);
     $this->assertTrue(Security::database_is_ready());
     Security::$force_database_is_ready = $old;
 }
 public function requireField()
 {
     $spec = DB::get_schema()->IdColumn(false, $this->getAutoIncrement());
     DB::require_field($this->getTable(), $this->getName(), $spec);
 }
 /**
  * Check that updates to a dataobject's indexes are reflected in DDL
  */
 public function testIndexesRerequestChanges()
 {
     $schema = DB::get_schema();
     $test = $this;
     DB::quiet();
     // Table will have been initially created by the $extraDataObjects setting
     // Update the SearchFields index here
     Config::inst()->update('DataObjectSchemaGenerationTest_IndexDO', 'indexes', array('SearchFields' => array('value' => 'Title')));
     // Verify that the above index change triggered a schema update
     $schema->schemaUpdate(function () use($test, $schema) {
         $obj = new DataObjectSchemaGenerationTest_IndexDO();
         $obj->requireTable();
         $needsUpdating = $schema->doesSchemaNeedUpdating();
         $schema->cancelSchemaUpdate();
         $test->assertTrue($needsUpdating);
     });
 }
Ejemplo n.º 11
0
 /**
  * Cleanup orphaned records in the _versions table
  *
  * @param string $baseTable base table to use as authoritative source of records
  * @param string $childTable Sub-table to clean orphans from
  */
 protected function cleanupVersionedOrphans($baseTable, $childTable)
 {
     // Skip if child table doesn't exist
     if (!DB::get_schema()->hasTable($childTable)) {
         return;
     }
     // Skip if tables are the same
     if ($childTable === $baseTable) {
         return;
     }
     // Select all orphaned version records
     $orphanedQuery = SQLSelect::create()->selectField("\"{$childTable}\".\"ID\"")->setFrom("\"{$childTable}\"");
     // If we have a parent table limit orphaned records
     // to only those that exist in this
     if (DB::get_schema()->hasTable($baseTable)) {
         $orphanedQuery->addLeftJoin($baseTable, "\"{$childTable}\".\"RecordID\" = \"{$baseTable}\".\"RecordID\"\n\t\t\t\t\tAND \"{$childTable}\".\"Version\" = \"{$baseTable}\".\"Version\"")->addWhere("\"{$baseTable}\".\"ID\" IS NULL");
     }
     $count = $orphanedQuery->count();
     if ($count > 0) {
         DB::alteration_message("Removing {$count} orphaned versioned records", "deleted");
         $ids = $orphanedQuery->execute()->column();
         foreach ($ids as $id) {
             DB::prepared_query("DELETE FROM \"{$childTable}\" WHERE \"ID\" = ?", array($id));
         }
     }
 }