diff --git a/include/upgrader/streams/core/26fd79dc-226da4e7.patch.sql b/include/upgrader/streams/core/26fd79dc-226da4e7.patch.sql
index e6dae706577e7323ca7a3539cf5e4ec6044ae3e4..b5d19699c2306c5d6bd2218cfa1bb036c4b31d73 100644
--- a/include/upgrader/streams/core/26fd79dc-226da4e7.patch.sql
+++ b/include/upgrader/streams/core/26fd79dc-226da4e7.patch.sql
@@ -18,6 +18,23 @@ CREATE TABLE `%TABLE_PREFIX%event` (
   UNIQUE KEY `name` (`name`)
 ) ENGINE=InnoDB  DEFAULT CHARSET=utf8;
 
+INSERT INTO `%TABLE_PREFIX%event` (`id`, `name`, `description`)
+VALUES
+	(1,'created',''),
+	(2,'closed',''),
+	(3,'reopened',''),
+	(4,'assigned',''),
+	(5,'released',''),
+	(6,'transferred',''),
+	(7,'referred',''),
+	(8,'overdue',''),
+	(9,'edited',''),
+	(10,'viewed',''),
+	(11,'error',''),
+	(12,'collab',''),
+	(13,'resent',''),
+	(14,'deleted','');
+
 -- Add event_id column to thread_events
 ALTER TABLE `%TABLE_PREFIX%thread_event`
     ADD `event_id` int(11) unsigned AFTER `thread_id`;
diff --git a/include/upgrader/streams/core/26fd79dc-226da4e7.task.php b/include/upgrader/streams/core/26fd79dc-226da4e7.task.php
index 3fb5b806562278ff391fba27e77561399044694d..e41b6c778bb32cb6551138bb3bfc5f1305488c2f 100644
--- a/include/upgrader/streams/core/26fd79dc-226da4e7.task.php
+++ b/include/upgrader/streams/core/26fd79dc-226da4e7.task.php
@@ -2,27 +2,141 @@
 
 class EventEnumRemoval extends MigrationTask {
     var $description = "Remove the Enum 'state' field from ThreadEvents";
+    var $queue;
+    var $skipList;
+    var $errorList = array();
 
-    function run() {
-        // Move states into the new table as events
-        $states = array('created','closed','reopened','assigned', 'released', 'transferred', 'referred', 'overdue','edited','viewed','error','collab','resent', 'deleted');
-        foreach ($states as $state) {
-            $sql= "INSERT INTO ".EVENT_TABLE." (`name`, `description`)
-                    VALUES('".
-                    $state. "', '')";
-            db_query($sql);
+    function sleep() {
+        return array('queue'=>$this->queue, 'skipList'=>$this->skipList);
+    }
+    function wakeup($stuff) {
+        $this->queue = $stuff['queue'];
+        $this->skipList = $stuff['skipList'];
+        while (!$this->isFinished())
+            $this->do_batch(30, 500);
+    }
+
+    function run($max_time) {
+        $this->do_batch($max_time * 0.9, 500);
+    }
+
+    function isFinished() {
+        return $this->getQueueLength() == 0;
+    }
+
+    function do_batch($time=30, $max=0) {
+        if(!$this->queueEvents($max) || !$this->getQueueLength())
+            return 0;
+
+        $this->setStatus("{$this->getQueueLength()} events remaining");
+
+        $count = 0;
+        $start = Misc::micro_time();
+        while ($this->getQueueLength() && (Misc::micro_time()-$start) < $time) {
+            if($this->next() && $max && ++$count>=$max) {
+                break;
+            }
+        }
+
+        return $this->queueEvents($max);
+    }
+
+    function queueEvents($limit=0){
+        global $cfg, $ost;
+
+        # Since the queue is persistent - we want to make sure we get to empty
+        # before we find more events.
+        if(($qc=$this->getQueueLength()))
+            return $qc;
+
+        $sql = "SELECT this.id, this.state, that.id, that.name FROM ".THREAD_EVENT_TABLE. " this
+            INNER JOIN (
+            SELECT id, name
+            FROM ". EVENT_TABLE. ") that
+            WHERE this.state = that.name
+            AND event_id IS NULL";
+
+        if(($skipList=$this->getSkipList()))
+            $sql.= ' AND this.id NOT IN('.implode(',', db_input($skipList)).')';
+
+        if($limit && is_numeric($limit))
+            $sql.=' LIMIT '.$limit;
+
+        //XXX: Do a hard fail or error querying the database?
+        if(!($res=db_query($sql)))
+            return $this->error('Unable to query DB for Thread Event migration!');
+
+        // Force the log message to the database
+        $ost->logDebug("Thread Event Migration", 'Found '.db_num_rows($res)
+            .' events to migrate', true);
+            if(!db_num_rows($res))
+                return 0;  //Nothing else to do!!
+
+        $this->queue = array();
+        while (list($id, $state, $eventId, $eventName)=db_fetch_row($res)) {
+            $info=array(
+                'id'        => $id,
+                'state'     => $state,
+                'eventId'   => $eventId,
+                'eventName' => $eventName,
+            );
+            $this->enqueue($info);
         }
 
-        $sql = "UPDATE ".THREAD_EVENT_TABLE. " AS this
-                INNER JOIN (
-                    SELECT id, name
-                    FROM ". EVENT_TABLE. ") AS that
-                SET this.event_id = that.id
-                WHERE this.state = that.name";
-        db_query($sql);
+        return $this->getQueueLength();
     }
 
+    function skip($eventId, $error) {
+        $this->skipList[] = $eventId;
+
+        return $this->error($error." (ID #$eventId)");
+    }
+
+    function error($what) {
+        global $ost;
+
+        $this->errors++;
+        $this->errorList[] = $what;
+        // Log the error but don't send the alert email
+        $ost->logError('Upgrader: Thread Event Migrater', $what, false);
+        # Assist in returning FALSE for inline returns with this method
+        return false;
+    }
+
+    function getErrors() {
+        return $this->errorList;
+    }
+
+    function getSkipList() {
+        return $this->skipList;
+    }
+
+    function enqueue($info) {
+        $this->queue[] = $info;
+    }
+
+    function getQueueLength() {
+        return count($this->queue);
+    }
+
+    function next() {
+        # Fetch next item -- use the last item so the array indices don't
+        # need to be recalculated for every shift() operation.
+        $info = array_pop($this->queue);
+
+        if (!$info['state']) {
+            # Continue with next thread event
+            return $this->skip($info['eventId'],
+            sprintf('Thread Event ID %s: State is blank', $info['eventId']));
+        }
+
+        db_query('update '.THREAD_EVENT_TABLE
+            .' set event_id='.db_input($info['eventId'])
+            .' where state='.db_input($info['eventName'])
+            .' and id='.db_input($info['id']));
+
+        return true;
+    }
 }
 return 'EventEnumRemoval';
-
 ?>