/** * @see self::testVersionedSiteTreeDeleteWithDeletableVersionedHasOneRelation() */ public function testVersionedSiteTreeDeleteWithDeletableVersionedHasOneRelationFromNewClassInstance() { // Make sure that has_many <= has_one relations are cleaned // for related DataObject classes even if the class type // being deleted does not have the relations. DataObjectOnDeleteDecorator::set_clean_versions_table(true); $masterPage = DataObject::get_one('DataObjectOnDeleteDecoratorTest_MasterPage', "\"Title\" = 'MasterPage'"); $this->assertInstanceOf('DataObjectOnDeleteDecoratorTest_MasterPage', $masterPage); $masterPageID = $masterPage->ID; $subPage = DataObject::get_one('DataObjectOnDeleteDecoratorTest_SubPage', "\"Title\" = 'SubPage'"); $this->assertInstanceOf('DataObjectOnDeleteDecoratorTest_SubPage', $subPage); $subPageID = $subPage->ID; $this->assertEquals($masterPage->ID, $subPage->MasterPageID, "Something is wrong with the fixture"); $subPage->doPublish(); $masterPage = $masterPage->newClassInstance('VirtualPage'); $masterPage->write(); $masterPage->delete(); $this->assertVersionedTables('SiteTree', $masterPageID, false); $this->assertVersionedTables('DataObjectOnDeleteDecoratorTest_SubPage', $subPageID, false); DataObjectOnDeleteDecorator::set_clean_versions_table(false); }
/** * Use DB plumber to backup the database. * * @return string The path to the database dump. */ public static function backup_database() { if (!self::available()) { trigger_error("This feature requires the Database Plumber module." . " See README.md for more information.", E_USER_ERROR); } if (!self::get_sql_file_path()) { trigger_error("You must set self::\$sql_file_path to use this feature.", E_USER_ERROR); } $SQLDialects = array( 'MySQLDatabase' => 'MySQL', 'SQLite3Database' => 'SQLite', 'MSSQLDatabase' => 'MSSQL', 'PostgreSQLDatabase' => 'Postgres' ); $sql = singleton('DBP_Database_Controller')->backup( DataObjectOnDeleteDecorator::get_db_tables(false), @$SQLDialects[get_class(DB::getConn())]); file_put_contents(self::get_sql_file_path(), $sql); return self::get_sql_file_path(); }
/** * Handle orphaned rows in intermediate many_many tables. */ public function handleBrokenManyManyRelations() { JanitorDebug::message("handleBrokenManyManyRelations()"); $dbTables = DataObjectOnDeleteDecorator::get_db_tables(); foreach ((array)$this->dataObject->many_many() as $relationName => $relationClass) { list($parentClass, $componentClass, $parentField, $componentField, $table) = $this->dataObject->many_many($relationName); if (!in_array($table, DataObjectOnDeleteDecorator_ManyManyCleaner::get_ignored_tables()) && in_array(strtolower($table), $dbTables)) { $oneWayTables = DataObjectOnDeleteDecorator_ManyManyCleaner::get_one_way_tables(); $oneWayTable = array_key_exists($table, $oneWayTables); if ($oneWayTable && $oneWayTables[$table] != $relationName) { JanitorDebug::message("Handling one-way table \"{$table}\" from \"{$relationName}\", aborting"); continue; } $query = "SELECT \"{$parentField}\" FROM \"{$table}\" WHERE 1=1"; $relations = DB::query($query)->column($parentField); foreach ((array)$relations as $ID) { $baseDataObject = DataObject::get_by_id($this->baseClassName, $ID); if ($baseDataObject) continue; $query = "DELETE FROM \"{$table}\" WHERE \"{$parentField}\" = {$ID}"; JanitorDebug::message("Running query: {$query}", 'p', 'color:#ff0000'); DB::query($query); } } } }
/** * Clean the intermediate table from references to $this->ID. */ protected function handleRelation($class, $classRelationName) { $dbTables = DataObjectOnDeleteDecorator::get_db_tables(); list($parentClass, $componentClass, $parentField, $componentField, $table) = $this->object->many_many($classRelationName); if (!in_array($table, self::get_ignored_tables()) && in_array(strtolower($table), $dbTables)) { $oneWayTables = self::get_one_way_tables(); $oneWayTable = array_key_exists($table, $oneWayTables); if (!$oneWayTable || ($oneWayTable && $oneWayTables[$table] == $classRelationName)) { DB::query("DELETE FROM \"{$table}\" WHERE \"{$parentField}\" = {$this->ID}"); } } }
public function testWithOrphanedManyManyRelations() { DataObjectOnDeleteDecorator::set_disabled(true); //JanitorDebug::set_verbose(true); $page1 = new Page(); $page1->write(); $page1->doPublish(); $page1ID = $page1->ID; $page2 = new Page(); $page2->Content .= "<p><a href=\"[sitetree_link id={$page1->ID}]\">page1 link</a></p>"; $page2->write(); $page2->doPublish(); $page2ID = $page2->ID; $query = "SELECT \"ID\" FROM \"SiteTree_LinkTracking\" WHERE \"SiteTreeID\" = $page2ID AND \"ChildID\" = $page1ID"; $page2->deleteFromStage('Live'); $page2 = DataObject::get_by_id('Page', $page2ID); $page2->delete(); $this->assertTrue((bool)DB::query($query)->value(), "many_many table SiteTree_LinkTracking cleaned prematurely (possibly due to SilverStripe core changes)"); DataObjectOnDeleteDecorator::set_disabled(false); $task = new DataObjectRetroactiveCleanerTask(); $task->run(null); $task->deleteBackup(); $this->assertFalse((bool)DB::query($query)->value(), "many_many table SiteTree_LinkTracking not cleaned properly (retroactively)"); JanitorDebug::set_verbose(false); }