fix_group_items_with_old_db.php 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. /**
  4. * Fix a very weird case where the following queries seem to have been executed
  5. * twice during a database migration from 1.9 to 1.10
  6. * 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);
  7. * 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);
  8. * These queries, as explained in BT#13243, when executed twice, break the
  9. * relationship of events to groups, and maybe other group resources, and then
  10. * deletes them.
  11. *
  12. * To fix, we need access to the previous database (version 1.9) and an active
  13. * connection to the migrated database (version 1.11 in this case). The script
  14. * scans through events in the old database, checks if they still exist in the
  15. * c_item_property in the new database, and if they don't tries to recreate the
  16. * c_item_property records needed to recover them.
  17. *
  18. * Due to the active use of the v1.11 configuration file through global.inc.php,
  19. * settings for the old database have to be added manually.
  20. */
  21. /**
  22. * Context initialization
  23. */
  24. if (PHP_SAPI != 'cli') {
  25. die('This script can only be executed from the command line');
  26. }
  27. require __DIR__.'/../../main/inc/global.inc.php';
  28. /**
  29. * Old database configuration
  30. */
  31. $oldDBHost = 'localhost';
  32. $oldDBName = 'db1.9';
  33. $oldDBUser = 'db1.9';
  34. $oldDBPass = 'db1.9';
  35. try {
  36. $oldDBH = new PDO('mysql:host=localhost;dbname='.$oldDBName, $oldDBUser, $oldDBPass);
  37. } catch (PDOException $e) {
  38. echo "Error connecting to old database: ".$e->getMessage().PHP_EOL;
  39. }
  40. /**
  41. * New database configuration
  42. */
  43. $newDBHost = 'localhost';
  44. $newDBName = 'db1.11';
  45. $newDBUser = 'db1.11';
  46. $newDBPass = 'db1.11';
  47. try {
  48. $newDBH = new PDO('mysql:host=localhost;dbname='.$newDBName, $newDBUser, $newDBPass);
  49. } catch (PDOException $e) {
  50. echo "Error connecting to new database: ".$e->getMessage().PHP_EOL;
  51. }
  52. /**
  53. * Start looking for group events
  54. */
  55. $foundCount = 0;
  56. $totalCount = 0;
  57. // Get a list of groups by course (TODO add sessions support)
  58. $groups = getOldGroupsByCourse();
  59. // Set this optional filter to something else than empty if you want to only
  60. // check items of one course
  61. $optionalCourseFilter = '';
  62. $sqlOld = "SELECT * FROM c_item_property WHERE to_group_id IS NOT NULL AND to_group_id != 0";
  63. if (!empty($optionalCourseFilter)) {
  64. $sqlOld .= ' AND c_id = '.$optionalCourseFilter;
  65. }
  66. foreach ($oldDBH->query($sqlOld) as $oldRow) {
  67. echo $oldRow['c_id'].' '.$oldRow['id'].' '.$oldRow['tool'].' '.$oldRow['ref'].' '.$oldRow['to_group_id'].PHP_EOL;
  68. $totalCount++;
  69. $sessionSubSelect = '';
  70. $sessionInsert = '';
  71. if (!empty($oldRow['id_session'])) {
  72. $sessionSubSelect = ' AND session_id = "'.$oldRow['id_session'].'" ';
  73. $sessionInsert = $oldRow['id_session'];
  74. } else {
  75. $sessionSubSelect = ' AND session_id IS NULL ';
  76. $sessionInsert = 'NULL';
  77. }
  78. $sqlNew = "SELECT iid, to_group_id FROM c_item_property
  79. WHERE c_id = ".$oldRow['c_id'].
  80. $sessionSubSelect.
  81. " AND tool = '".$oldRow['tool']."'
  82. AND ref = ".$oldRow['ref'];
  83. //echo trim(str_replace("\n", '', $sqlNew)).PHP_EOL;
  84. $q = $newDBH->query($sqlNew);
  85. // Two situations arise: the record is found and is pointing to the wrong group -> update
  86. // or the record is not found and we need to create it based on the new iid of the old group's id -> insert
  87. if ($q->rowCount() > 0) {
  88. $newRow = $q->fetch();
  89. //echo "--> Found corresponding c_item_property as ".$newRow['iid'].PHP_EOL;
  90. $foundCount++;
  91. // First check if the group referenced in the old database still exists.
  92. // This is originally the c_group_info.id, NOT the c_group_info.iid, so
  93. // we need to check the existence of a group with that id in course c_id
  94. // If the group doesn't exist anymore, skip the update/insertion (maybe
  95. // we should even delete it?)
  96. if (isset($groups[$oldRow['c_id']][$oldRow['to_group_id']])) {
  97. $newGroupId = $groups[$oldRow['c_id']][$oldRow['to_group_id']];
  98. // also check if the new ID is different, otherwise we can avoid an update
  99. if ($oldRow['to_group_id'] != $newGroupId) {
  100. // Update this row to attach it to the right group
  101. $sqlFix = 'UPDATE c_item_property SET to_group_id = '.$newGroupId.' WHERE iid = '.$newRow['iid'];
  102. //echo $sqlFix.PHP_EOL;
  103. $newDBH->query($sqlFix);
  104. }
  105. }
  106. } else {
  107. echo "xx> No corresponding c_item_property found".PHP_EOL;
  108. // First check if the group referenced in the old database still exists.
  109. // This is originally the c_group_info.id, NOT the c_group_info.iid, so
  110. // we need to check the existence of a group with that id in course c_id
  111. // If the group doesn't exist anymore, skip the update/insertion (maybe
  112. // we should even delete it?)
  113. if (isset($groups[$oldRow['c_id']][$oldRow['to_group_id']])) {
  114. $newGroupId = $groups[$oldRow['c_id']][$oldRow['to_group_id']];
  115. // Insert a new row to make the group calendar event visible again
  116. $sqlFix = 'INSERT INTO c_item_property(c_id, to_group_id, to_user_id, '.
  117. ' insert_user_id, session_id, tool, insert_date, lastedit_date, ref,'.
  118. ' lastedit_type, lastedit_user_id, visibility, start_visible, end_visible)'.
  119. " VALUES ("
  120. .$oldRow['c_id'].', '
  121. .$newGroupId.', '
  122. .' NULL, '
  123. .$oldRow['insert_user_id'].', '
  124. .$sessionInsert.', '
  125. .'\''.$oldRow['tool'].'\', '
  126. .'\''.$oldRow['insert_date'].'\', '
  127. .'\''.$oldRow['lastedit_date'].'\', '
  128. .$oldRow['ref'].', '
  129. .'\''.$oldRow['lastedit_type'].'\', '
  130. .$oldRow['lastedit_user_id'].', '
  131. .$oldRow['visibility'].', '
  132. .'\''.$oldRow['start_visible'].'\', '
  133. .'\''.$oldRow['end_visible'].'\''
  134. .')';
  135. //echo $sqlFix.PHP_EOL;
  136. $newDBH->query($sqlFix);
  137. }
  138. }
  139. }
  140. echo PHP_EOL;
  141. $diff = $totalCount - $foundCount;
  142. echo "Found $foundCount corresponding c_item_property on a total of $totalCount items searched for (we're missing $diff)".PHP_EOL;
  143. /**
  144. * Helper function to get a list of existing groups from the c_group_info table
  145. * @return array
  146. */
  147. function getOldGroupsByCourse() {
  148. global $oldDBH;
  149. global $newDBH;
  150. $groups = [];
  151. $courses = [];
  152. $sql = "SELECT id FROM course";
  153. foreach ($oldDBH->query($sql) as $course) {
  154. $sqlGroup = "SELECT id, iid FROM c_group_info WHERE c_id = ".$course['id'];
  155. foreach ($newDBH->query($sqlGroup) as $group) {
  156. if (!isset($courses[$course['id']])) {
  157. $courses[$course['id']] = [];
  158. }
  159. $courses[$course['id']][$group['id']] = $group['iid'];
  160. }
  161. }
  162. return $courses;
  163. }