From ebca2f9a854a13b329bd3baf988038a04a5fe750 Mon Sep 17 00:00:00 2001
From: aydreeihn <adriane@enhancesoft.com>
Date: Tue, 14 Aug 2018 12:39:40 -0500
Subject: [PATCH] Optimize Upgrade: Remove ThreadEvent 'state' Enum

- Create a new database patch to add a new Event table, use event_id instead of state for thread_events, and remote the 'state' column in thread_events
- Create the Event class
- Use event_id instead of state for Thread Events
---
 bootstrap.php                                 |  1 +
 include/class.report.php                      | 30 +++++++-----
 include/class.thread.php                      | 47 ++++++++++++++++++-
 include/class.user.php                        |  3 +-
 .../client/templates/thread-entries.tmpl.php  |  8 +++-
 include/upgrader/streams/core.sig             |  2 +-
 .../streams/core/0ca85857-86707325.patch.sql  |  4 --
 .../core/26fd79dc-226da4e7.cleanup.sql        |  3 ++
 .../streams/core/26fd79dc-226da4e7.patch.sql  | 28 +++++++++++
 .../streams/core/26fd79dc-226da4e7.task.php   | 28 +++++++++++
 .../streams/core/70921d5c-26fd79dc.patch.sql  |  4 --
 setup/inc/streams/core/install-mysql.sql      |  9 ++++
 12 files changed, 142 insertions(+), 25 deletions(-)
 create mode 100644 include/upgrader/streams/core/26fd79dc-226da4e7.cleanup.sql
 create mode 100644 include/upgrader/streams/core/26fd79dc-226da4e7.patch.sql
 create mode 100644 include/upgrader/streams/core/26fd79dc-226da4e7.task.php

diff --git a/bootstrap.php b/bootstrap.php
index 4b64227a8..085ad9dc1 100644
--- a/bootstrap.php
+++ b/bootstrap.php
@@ -102,6 +102,7 @@ class Bootstrap {
         define('THREAD_COLLABORATOR_TABLE', $prefix.'thread_collaborator');
         define('TICKET_STATUS_TABLE', $prefix.'ticket_status');
         define('TICKET_PRIORITY_TABLE',$prefix.'ticket_priority');
+        define('EVENT_TABLE',$prefix.'event');
 
         define('TASK_TABLE', $prefix.'task');
         define('TASK_CDATA_TABLE', $prefix.'task__cdata');
diff --git a/include/class.report.php b/include/class.report.php
index 12d7322d3..c2aa391ef 100644
--- a/include/class.report.php
+++ b/include/class.report.php
@@ -77,26 +77,32 @@ class OverviewReport {
 
     function getPlotData() {
         list($start, $stop) = $this->getDateRange();
+        $states = array("created", "closed", "reopened", "assigned", "overdue", "transferred");
+        $event_ids = array();
+        foreach ($states as $state) {
+            $eid = Event::getIdByName($state);
+            $event_ids[] = $eid;
+        }
 
         # Fetch all types of events over the timeframe
         $res = db_query('SELECT DISTINCT(state) FROM '.THREAD_EVENT_TABLE
             .' WHERE timestamp BETWEEN '.$start.' AND '.$stop
-            .' AND state IN ("created", "closed", "reopened", "assigned", "overdue", "transferred")'
+            .' AND event_id IN ('.implode($event_ids).')'
             .' ORDER BY 1');
         $events = array();
         while ($row = db_fetch_row($res)) $events[] = $row[0];
 
         # TODO: Handle user => db timezone offset
         # XXX: Implement annulled column from the %ticket_event table
-        $res = db_query('SELECT state, DATE_FORMAT(timestamp, \'%Y-%m-%d\'), '
+        $res = db_query('SELECT event_id, DATE_FORMAT(timestamp, \'%Y-%m-%d\'), '
                 .'COUNT(DISTINCT T.id)'
             .' FROM '.THREAD_EVENT_TABLE. ' E '
             .' JOIN '.THREAD_TABLE. ' T
                 ON (T.id = E.thread_id AND T.object_type = "T") '
             .' WHERE E.timestamp BETWEEN '.$start.' AND '.$stop
             .' AND NOT annulled'
-            .' AND E.state IN ("created", "closed", "reopened", "assigned", "overdue", "transferred")'
-            .' GROUP BY E.state, DATE_FORMAT(E.timestamp, \'%Y-%m-%d\')'
+            .' AND E.event_id IN ('.implode($event_ids).')'
+            .' GROUP BY E.event_id, DATE_FORMAT(E.timestamp, \'%Y-%m-%d\')'
             .' ORDER BY 2, 1');
         # Initialize array of plot values
         $plots = array();
@@ -148,8 +154,8 @@ class OverviewReport {
                ))
             ->constrain(array(
                 'thread__events' => array(
-                    'thread__events__state' => 'created',
-                    'state' => 'closed',
+                    'thread__events__event_id' => Event::getIdByName('created'),
+                    'event_id' => Event::getIdByName('closed'),
                     'annulled' => 0,
                     ),
                 ))
@@ -174,27 +180,27 @@ class OverviewReport {
                 ->aggregate(array(
                     'Opened' => SqlAggregate::COUNT(
                         SqlCase::N()
-                            ->when(new Q(array('state' => 'created')), 1)
+                            ->when(new Q(array('event_id' => Event::getIdByName('created'))), 1)
                     ),
                     'Assigned' => SqlAggregate::COUNT(
                         SqlCase::N()
-                            ->when(new Q(array('state' => 'assigned')), 1)
+                            ->when(new Q(array('event_id' => Event::getIdByName('assigned'))), 1)
                     ),
                     'Overdue' => SqlAggregate::COUNT(
                         SqlCase::N()
-                            ->when(new Q(array('state' => 'overdue')), 1)
+                            ->when(new Q(array('event_id' => Event::getIdByName('overdue'))), 1)
                     ),
                     'Closed' => SqlAggregate::COUNT(
                         SqlCase::N()
-                            ->when(new Q(array('state' => 'closed')), 1)
+                            ->when(new Q(array('event_id' => Event::getIdByName('closed'))), 1)
                     ),
                     'Reopened' => SqlAggregate::COUNT(
                         SqlCase::N()
-                            ->when(new Q(array('state' => 'reopened')), 1)
+                            ->when(new Q(array('event_id' => Event::getIdByName('reopened'))), 1)
                     ),
                     'Deleted' => SqlAggregate::COUNT(
                         SqlCase::N()
-                            ->when(new Q(array('state' => 'deleted')), 1)
+                            ->when(new Q(array('event_id' => Event::getIdByName('deleted'))), 1)
                     ),
                 ));
 
diff --git a/include/class.thread.php b/include/class.thread.php
index 2aa82cd49..0807834b3 100644
--- a/include/class.thread.php
+++ b/include/class.thread.php
@@ -2049,16 +2049,59 @@ class ThreadEvent extends VerySimpleModel {
                     $subclasses[$class::$state] = $class;
             }
         }
+        $this->state = Event::getNameById($this->event_id);
         if (!($class = $subclasses[$this->state]))
             return $this;
         return new $class($this->ht);
     }
 }
 
+class Event extends VerySimpleModel {
+    static $meta = array(
+        'table' => EVENT_TABLE,
+        'pk' => array('id'),
+    );
+
+    function getInfo() {
+        return $this->ht;
+    }
+
+    function getId() {
+        return $this->id;
+    }
+
+    function getName() {
+        return $this->name;
+    }
+
+    function getNameById($id) {
+        $row = Event::objects()
+            ->filter(array('id'=>$id))
+            ->values_flat('name')
+            ->first();
+
+        return $row ? $row[0] : 0;
+    }
+
+    function getIdByName($name) {
+        $row = Event::objects()
+            ->filter(array('name'=>$name))
+            ->values_flat('id')
+            ->first();
+
+        return $row ? $row[0] : 0;
+    }
+
+    function getDescription() {
+        return $this->description;
+    }
+}
+
 class ThreadEvents extends InstrumentedList {
     function annul($event) {
+        $event_id = Event::getIdByName($event);
         $this->queryset
-            ->filter(array('state' => $event))
+            ->filter(array('event_id' => $event_id))
             ->update(array('annulled' => 1));
     }
 
@@ -2113,7 +2156,7 @@ class ThreadEvents extends InstrumentedList {
             }
         }
         $event->username = $username;
-        $event->state = $state;
+        $event->event_id = Event::getIdByName($state);
 
         if ($data) {
             if (is_array($data))
diff --git a/include/class.user.php b/include/class.user.php
index 53a8627c3..d9ca8463e 100644
--- a/include/class.user.php
+++ b/include/class.user.php
@@ -635,7 +635,8 @@ implements TemplateVariable, Searchable {
     }
 
     function deleteAllTickets() {
-        $deleted = TicketStatus::lookup(array('state' => 'deleted'));
+        $event_id = Event::getIdByName('deleted');
+        $deleted = TicketStatus::lookup(array('event_id' => $event_id));
         foreach($this->tickets as $ticket) {
             if (!$T = Ticket::lookup($ticket->getId()))
                 continue;
diff --git a/include/client/templates/thread-entries.tmpl.php b/include/client/templates/thread-entries.tmpl.php
index 938432109..8457698ca 100644
--- a/include/client/templates/thread-entries.tmpl.php
+++ b/include/client/templates/thread-entries.tmpl.php
@@ -1,6 +1,12 @@
 <?php
+$states = array('created', 'closed', 'reopened', 'edited', 'collab');
+$event_ids = array();
+foreach ($states as $state) {
+    $eid = Event::getIdByName($state);
+    $event_ids[] = $eid;
+}
 $events = $events
-    ->filter(array('state__in' => array('created', 'closed', 'reopened', 'edited', 'collab')))
+    ->filter(array('event_id__in' => $event_ids))
     ->order_by('id');
 $events = new IteratorIterator($events->getIterator());
 $events->rewind();
diff --git a/include/upgrader/streams/core.sig b/include/upgrader/streams/core.sig
index fbebb45e6..cd7008c3c 100644
--- a/include/upgrader/streams/core.sig
+++ b/include/upgrader/streams/core.sig
@@ -1 +1 @@
-26fd79dc5443f37779f9d2c4108058f4
+226da4e7298917160c7499cb63370f83
diff --git a/include/upgrader/streams/core/0ca85857-86707325.patch.sql b/include/upgrader/streams/core/0ca85857-86707325.patch.sql
index 2962d23e4..2daaf3b7c 100644
--- a/include/upgrader/streams/core/0ca85857-86707325.patch.sql
+++ b/include/upgrader/streams/core/0ca85857-86707325.patch.sql
@@ -17,10 +17,6 @@ CREATE TABLE `%TABLE_PREFIX%thread_referral` (
   KEY `thread_id` (`thread_id`)
 ) ENGINE=InnoDB  DEFAULT CHARSET=utf8;
 
-ALTER TABLE `%TABLE_PREFIX%thread_event`
-  CHANGE `state` `state` enum('created','closed','reopened','assigned','transferred', 'referred', 'overdue','edited','viewed','error','collab','resent', 'deleted') NOT NULL;
-
-
  -- Finished with patch
 UPDATE `%TABLE_PREFIX%config`
     SET `value` = '86707325fc571e56242fccc46fd24466'
diff --git a/include/upgrader/streams/core/26fd79dc-226da4e7.cleanup.sql b/include/upgrader/streams/core/26fd79dc-226da4e7.cleanup.sql
new file mode 100644
index 000000000..50cad9d04
--- /dev/null
+++ b/include/upgrader/streams/core/26fd79dc-226da4e7.cleanup.sql
@@ -0,0 +1,3 @@
+-- Drop the state field from thread_events
+ALTER TABLE `%TABLE_PREFIX%thread_event`
+    DROP COLUMN `state`;
diff --git a/include/upgrader/streams/core/26fd79dc-226da4e7.patch.sql b/include/upgrader/streams/core/26fd79dc-226da4e7.patch.sql
new file mode 100644
index 000000000..e6dae7065
--- /dev/null
+++ b/include/upgrader/streams/core/26fd79dc-226da4e7.patch.sql
@@ -0,0 +1,28 @@
+/**
+* @signature 226da4e7298917160c7499cb63370f83
+* @version v1.11.0
+* @title Database Optimization
+*
+* This patch is for optimizing our database to handle large amounts of data
+* more smoothly.
+*
+* 1. remove states in thread_event table and add them to their own event table
+*/
+
+-- Create a new table to store events
+CREATE TABLE `%TABLE_PREFIX%event` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `name` varchar(60) NOT NULL,
+  `description` varchar(60) DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `name` (`name`)
+) ENGINE=InnoDB  DEFAULT CHARSET=utf8;
+
+-- Add event_id column to thread_events
+ALTER TABLE `%TABLE_PREFIX%thread_event`
+    ADD `event_id` int(11) unsigned AFTER `thread_id`;
+
+-- Finished with patch
+UPDATE `%TABLE_PREFIX%config`
+   SET `value` = '226da4e7298917160c7499cb63370f83', `updated` = NOW()
+   WHERE `key` = 'schema_signature' AND `namespace` = 'core';
diff --git a/include/upgrader/streams/core/26fd79dc-226da4e7.task.php b/include/upgrader/streams/core/26fd79dc-226da4e7.task.php
new file mode 100644
index 000000000..3fb5b8065
--- /dev/null
+++ b/include/upgrader/streams/core/26fd79dc-226da4e7.task.php
@@ -0,0 +1,28 @@
+<?php
+
+class EventEnumRemoval extends MigrationTask {
+    var $description = "Remove the Enum 'state' field from ThreadEvents";
+
+    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);
+        }
+
+        $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 'EventEnumRemoval';
+
+?>
diff --git a/include/upgrader/streams/core/70921d5c-26fd79dc.patch.sql b/include/upgrader/streams/core/70921d5c-26fd79dc.patch.sql
index f3bc7e0b2..fd5953f5b 100644
--- a/include/upgrader/streams/core/70921d5c-26fd79dc.patch.sql
+++ b/include/upgrader/streams/core/70921d5c-26fd79dc.patch.sql
@@ -5,10 +5,6 @@
 *
 * This patch is for final revisions needed for v1.11
 */
-
-ALTER TABLE `%TABLE_PREFIX%thread_event`
-    CHANGE `state` `state` enum('created','closed','reopened','assigned', 'released', 'transferred', 'referred', 'overdue','edited','viewed','error','collab','resent', 'deleted') NOT NULL;
-
 ALTER TABLE `%TABLE_PREFIX%attachment`
     ADD INDEX `file_object` (`file_id`,`object_id`);
 
diff --git a/setup/inc/streams/core/install-mysql.sql b/setup/inc/streams/core/install-mysql.sql
index fbbb6e450..bb13f3944 100644
--- a/setup/inc/streams/core/install-mysql.sql
+++ b/setup/inc/streams/core/install-mysql.sql
@@ -711,6 +711,15 @@ CREATE TABLE `%TABLE_PREFIX%lock` (
   KEY `staff_id` (`staff_id`)
 ) DEFAULT CHARSET=utf8;
 
+DROP TABLE IF EXISTS `%TABLE_PREFIX%event`;
+CREATE TABLE `%TABLE_PREFIX%event` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `name` varchar(60) NOT NULL,
+  `description` varchar(60) DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `name` (`name`),
+) ENGINE=InnoDB  DEFAULT CHARSET=utf8;
+
 DROP TABLE IF EXISTS `%TABLE_PREFIX%thread_event`;
 CREATE TABLE `%TABLE_PREFIX%thread_event` (
   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
-- 
GitLab