/**
  * {@inheritdoc}
  */
 public function rollback()
 {
     // Only begin the rollback operation if the migration is currently idle.
     if ($this->migration->getStatus() !== MigrationInterface::STATUS_IDLE) {
         $this->message->display($this->t('Migration @id is busy with another operation: @status', ['@id' => $this->migration->id(), '@status' => $this->t($this->migration->getStatusLabel())]), 'error');
         return MigrationInterface::RESULT_FAILED;
     }
     // Announce that rollback is about to happen.
     $this->getEventDispatcher()->dispatch(MigrateEvents::PRE_ROLLBACK, new MigrateRollbackEvent($this->migration));
     // Optimistically assume things are going to work out; if not, $return will be
     // updated to some other status.
     $return = MigrationInterface::RESULT_COMPLETED;
     $this->migration->setStatus(MigrationInterface::STATUS_ROLLING_BACK);
     $id_map = $this->migration->getIdMap();
     $destination = $this->migration->getDestinationPlugin();
     // Loop through each row in the map, and try to roll it back.
     foreach ($id_map as $map_row) {
         $destination_key = $id_map->currentDestination();
         if ($destination_key) {
             $map_row = $id_map->getRowByDestination($destination_key);
             if ($map_row['rollback_action'] == MigrateIdMapInterface::ROLLBACK_DELETE) {
                 $this->getEventDispatcher()->dispatch(MigrateEvents::PRE_ROW_DELETE, new MigrateRowDeleteEvent($this->migration, $destination_key));
                 $destination->rollback($destination_key);
                 $this->getEventDispatcher()->dispatch(MigrateEvents::POST_ROW_DELETE, new MigrateRowDeleteEvent($this->migration, $destination_key));
             }
             // We're now done with this row, so remove it from the map.
             $id_map->deleteDestination($destination_key);
         } else {
             // If there is no destination key the import probably failed and we can
             // remove the row without further action.
             $source_key = $id_map->currentSource();
             $id_map->delete($source_key);
         }
         // Check for memory exhaustion.
         if (($return = $this->checkStatus()) != MigrationInterface::RESULT_COMPLETED) {
             break;
         }
         // If anyone has requested we stop, return the requested result.
         if ($this->migration->getStatus() == MigrationInterface::STATUS_STOPPING) {
             $return = $this->migration->getInterruptionResult();
             $this->migration->clearInterruptionResult();
             break;
         }
     }
     // If rollback completed successfully, reset the high water mark.
     if ($return == MigrationInterface::RESULT_COMPLETED) {
         $this->migration->saveHighWater(NULL);
     }
     // Notify modules that rollback attempt was complete.
     $this->getEventDispatcher()->dispatch(MigrateEvents::POST_ROLLBACK, new MigrateRollbackEvent($this->migration));
     $this->migration->setStatus(MigrationInterface::STATUS_IDLE);
     return $return;
 }
Example #2
0
 /**
  * Create the map and message tables if they don't already exist.
  */
 protected function ensureTables()
 {
     if (!$this->getDatabase()->schema()->tableExists($this->mapTableName)) {
         // Generate appropriate schema info for the map and message tables,
         // and map from the source field names to the map/msg field names.
         $count = 1;
         $source_id_schema = array();
         foreach ($this->migration->getSourcePlugin()->getIds() as $id_definition) {
             $mapkey = 'sourceid' . $count++;
             $source_id_schema[$mapkey] = $this->getFieldSchema($id_definition);
             $source_id_schema[$mapkey]['not null'] = TRUE;
         }
         $source_ids_hash[static::SOURCE_IDS_HASH] = array('type' => 'varchar', 'length' => '64', 'not null' => TRUE, 'description' => 'Hash of source ids. Used as primary key');
         $fields = $source_ids_hash + $source_id_schema;
         // Add destination identifiers to map table.
         // @todo How do we discover the destination schema?
         $count = 1;
         foreach ($this->migration->getDestinationPlugin()->getIds() as $id_definition) {
             // Allow dest identifier fields to be NULL (for IGNORED/FAILED cases).
             $mapkey = 'destid' . $count++;
             $fields[$mapkey] = $this->getFieldSchema($id_definition);
             $fields[$mapkey]['not null'] = FALSE;
         }
         $fields['source_row_status'] = array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => MigrateIdMapInterface::STATUS_IMPORTED, 'description' => 'Indicates current status of the source row');
         $fields['rollback_action'] = array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => MigrateIdMapInterface::ROLLBACK_DELETE, 'description' => 'Flag indicating what to do for this item on rollback');
         $fields['last_imported'] = array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'description' => 'UNIX timestamp of the last time this row was imported');
         $fields['hash'] = array('type' => 'varchar', 'length' => '64', 'not null' => FALSE, 'description' => 'Hash of source row data, for detecting changes');
         $schema = array('description' => 'Mappings from source identifier value(s) to destination identifier value(s).', 'fields' => $fields, 'primary key' => array(static::SOURCE_IDS_HASH));
         $this->getDatabase()->schema()->createTable($this->mapTableName, $schema);
         // Now do the message table.
         if (!$this->getDatabase()->schema()->tableExists($this->messageTableName())) {
             $fields = array();
             $fields['msgid'] = array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE);
             $fields += $source_ids_hash;
             $fields['level'] = array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 1);
             $fields['message'] = array('type' => 'text', 'size' => 'medium', 'not null' => TRUE);
             $schema = array('description' => 'Messages generated during a migration process', 'fields' => $fields, 'primary key' => array('msgid'));
             $this->getDatabase()->schema()->createTable($this->messageTableName(), $schema);
         }
     } else {
         // Add any missing columns to the map table.
         if (!$this->getDatabase()->schema()->fieldExists($this->mapTableName, 'rollback_action')) {
             $this->getDatabase()->schema()->addField($this->mapTableName, 'rollback_action', array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, 'description' => 'Flag indicating what to do for this item on rollback'));
         }
         if (!$this->getDatabase()->schema()->fieldExists($this->mapTableName, 'hash')) {
             $this->getDatabase()->schema()->addField($this->mapTableName, 'hash', array('type' => 'varchar', 'length' => '64', 'not null' => FALSE, 'description' => 'Hash of source row data, for detecting changes'));
         }
         if (!$this->getDatabase()->schema()->fieldExists($this->mapTableName, static::SOURCE_IDS_HASH)) {
             $this->getDatabase()->schema()->addField($this->mapTableName, static::SOURCE_IDS_HASH, array('type' => 'varchar', 'length' => '64', 'not null' => TRUE, 'description' => 'Hash of source ids. Used as primary key'));
         }
     }
 }