Browse Source

Add feature to fix possible migration issues if migration 1.9 -> 1.10 has been partly executed twice (can cause damage to the references to groups in c_item_property) - refs BT#13243

Yannick Warnier 7 years ago
parent
commit
166604db7a
1 changed files with 176 additions and 0 deletions
  1. 176 0
      tests/scripts/fix_group_items_with_old_db.php

+ 176 - 0
tests/scripts/fix_group_items_with_old_db.php

@@ -0,0 +1,176 @@
+<?php
+/* For licensing terms, see /license.txt */
+/**
+ * Fix a very weird case where the following queries seem to have been executed
+ * twice during a database migration from 1.9 to 1.10
+ * UPDATE c_item_property cip SET cip.to_group_id = (SELECT cgi.iid FROM c_group_info cgi WHERE cgi.c_id = cip.c_id AND cgi.id = cip.to_group_id);
+ * DELETE FROM c_item_property WHERE to_group_id IS NOT NULL AND to_group_id <> 0 AND to_group_id NOT IN (SELECT iid FROM c_group_info);
+ * These queries, as explained in BT#13243, when executed twice, break the
+ * relationship of events to groups, and maybe other group resources, and then
+ * deletes them.
+ *
+ * To fix, we need access to the previous database (version 1.9) and an active
+ * connection to the migrated database (version 1.11 in this case). The script
+ * scans through events in the old database, checks if they still exist in the
+ * c_item_property in the new database, and if they don't tries to recreate the
+ * c_item_property records needed to recover them.
+ *
+ * Due to the active use of the v1.11 configuration file through global.inc.php,
+ * settings for the old database have to be added manually.
+ */
+/**
+ * Context initialization
+ */
+if (PHP_SAPI != 'cli') {
+    die('This script can only be executed from the command line');
+}
+require __DIR__.'/../../main/inc/global.inc.php';
+
+/**
+ * Old database configuration
+ */
+$oldDBHost = 'localhost';
+$oldDBName = 'carmelitas19';
+$oldDBUser = 'carmelitas19';
+$oldDBPass = 'carmelitas19';
+try {
+    $oldDBH = new PDO('mysql:host=localhost;dbname='.$oldDBName, $oldDBUser, $oldDBPass);
+} catch (PDOException $e) {
+    echo "Error connecting to old database: ".$e->getMessage().PHP_EOL;
+}
+
+/**
+ * New database configuration
+ */
+$newDBHost = 'localhost';
+$newDBName = 'carmelitas11';
+$newDBUser = 'carmelitas11';
+$newDBPass = 'carmelitas11';
+try {
+    $newDBH = new PDO('mysql:host=localhost;dbname='.$newDBName, $newDBUser, $newDBPass);
+} catch (PDOException $e) {
+    echo "Error connecting to new database: ".$e->getMessage().PHP_EOL;
+}
+
+/**
+ * Start looking for group events
+ */
+$foundCount = 0;
+$totalCount = 0;
+// Get a list of groups by course (TODO add sessions support)
+$groups = getOldGroupsByCourse();
+
+// Set this optional filter to something else than empty if you want to only
+// check items of one course
+$optionalCourseFilter = '11';
+$sqlOld = "SELECT * FROM c_item_property WHERE to_group_id IS NOT NULL AND to_group_id != 0";
+if (!empty($optionalCourseFilter)) {
+    $sqlOld .= ' AND c_id = '.$optionalCourseFilter;
+}
+foreach ($oldDBH->query($sqlOld) as $oldRow) {
+    echo $oldRow['c_id'].' '.$oldRow['id'].' '.$oldRow['tool'].' '.$oldRow['ref'].' '.$oldRow['to_group_id'].PHP_EOL;
+    $totalCount++;
+
+    $sessionSubSelect = '';
+    $sessionInsert = '';
+    if (!empty($oldRow['id_session'])) {
+        $sessionSubSelect = ' AND session_id = "'.$oldRow['id_session'].'" ';
+        $sessionInsert = $oldRow['id_session'];
+    } else {
+        $sessionSubSelect = ' AND session_id IS NULL ';
+        $sessionInsert = 'NULL';
+    }
+    $sqlNew = "SELECT iid, to_group_id FROM c_item_property
+        WHERE c_id = ".$oldRow['c_id'].
+        $sessionSubSelect.
+        " AND tool = '".$oldRow['tool']."' 
+        AND ref = ".$oldRow['ref'];
+
+    //echo trim(str_replace("\n", '', $sqlNew)).PHP_EOL;
+    $q = $newDBH->query($sqlNew);
+    // Two situations arise: the record is found and is pointing to the wrong group -> update
+    // or the record is not found and we need to create it based on the new iid of the old group's id -> insert
+    if ($q->rowCount() > 0) {
+        $newRow = $q->fetch();
+        //echo "--> Found corresponding c_item_property as ".$newRow['iid'].PHP_EOL;
+        $foundCount++;
+
+        // First check if the group referenced in the old database still exists.
+        // This is originally the c_group_info.id, NOT the c_group_info.iid, so
+        // we need to check the existence of a group with that id in course c_id
+        // If the group doesn't exist anymore, skip the update/insertion (maybe
+        // we should even delete it?)
+
+        if (isset($groups[$oldRow['c_id']][$oldRow['to_group_id']])) {
+            $newGroupId = $groups[$oldRow['c_id']][$oldRow['to_group_id']];
+            // also check if the new ID is different, otherwise we can avoid an update
+            if ($oldRow['to_group_id'] != $newGroupId) {
+                // Update this row to attach it to the right group
+                $sqlFix = 'UPDATE c_item_property SET to_group_id = '.$newGroupId.' WHERE iid = '.$newRow['iid'];
+                //echo $sqlFix.PHP_EOL;
+                $newDBH->query($sqlFix);
+            }
+        }
+    } else {
+        //echo "xx> No corresponding c_item_property found".PHP_EOL;
+
+        // First check if the group referenced in the old database still exists.
+        // This is originally the c_group_info.id, NOT the c_group_info.iid, so
+        // we need to check the existence of a group with that id in course c_id
+        // If the group doesn't exist anymore, skip the update/insertion (maybe
+        // we should even delete it?)
+
+        if (isset($groups[$oldRow['c_id']][$oldRow['to_group_id']])) {
+            $newGroupId = $groups[$oldRow['c_id']][$oldRow['to_group_id']];
+
+            // Insert a new row to make the group calendar event visible again
+            $sqlFix = 'INSERT INTO c_item_property(c_id, to_group_id, to_user_id, '.
+                ' insert_user_id, session_id, tool, insert_date, lastedit_date, ref,'.
+                ' lastedit_type, lastedit_user_id, visibility, start_visible, end_visible)'.
+                " VALUES ("
+                .$oldRow['c_id'].', '
+                .$newGroupId.', '
+                .' NULL, '
+                .$oldRow['insert_user_id'].', '
+                .$sessionInsert.', '
+                .'\''.$oldRow['tool'].'\', '
+                .'\''.$oldRow['insert_date'].'\', '
+                .'\''.$oldRow['lastedit_date'].'\', '
+                .$oldRow['ref'].', '
+                .'\''.$oldRow['lastedit_type'].'\', '
+                .$oldRow['lastedit_user_id'].', '
+                .$oldRow['visibility'].', '
+                .'\''.$oldRow['start_visible'].'\', '
+                .'\''.$oldRow['end_visible'].'\''
+                .')';
+            //echo $sqlFix.PHP_EOL;
+            $newDBH->query($sqlFix);
+        }
+    }
+}
+
+echo PHP_EOL;
+$diff = $totalCount - $foundCount;
+echo "Found $foundCount corresponding c_item_property on a total of $totalCount items searched for (we're missing $diff)".PHP_EOL;
+
+/**
+ * Helper function to get a list of existing groups from the c_group_info table
+ * @return array
+ */
+function getOldGroupsByCourse() {
+    global $oldDBH;
+    global $newDBH;
+    $groups = [];
+    $courses = [];
+    $sql = "SELECT id FROM course";
+    foreach ($oldDBH->query($sql) as $course) {
+        $sqlGroup = "SELECT id, iid FROM c_group_info WHERE c_id = ".$course['id'];
+        foreach ($newDBH->query($sqlGroup) as $group) {
+            if (!isset($courses[$course['id']])) {
+                $courses[$course['id']] = [];
+            }
+            $courses[$course['id']][$group['id']] = $group['iid'];
+        }
+    }
+    return $courses;
+}