ILIAS  trunk Revision v13.0_alpha-52-g21c366bbdd4
class.ilBackgroundTasksDBHotfixes10.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
24{
25 private const TABLE_BUCKET = 'il_bt_bucket';
26 private const TABLE_TASK = 'il_bt_task';
27 private const TABLE_VALUE = 'il_bt_value';
28 private const TABLE_VALUE_TO_TASK = 'il_bt_value_to_task';
29
30 private const FOREIGN_KEY_BUCKET_ROOT_TASK = 'il_bt_bucket_fk_root_task';
31 private const FOREIGN_KEY_BUCKET_CURRENT_TASK = 'il_bt_bucket_fk_current_task';
32 private const FOREIGN_KEY_TASK_BUCKET = 'il_bt_task_fk_bucket';
33 private const FOREIGN_KEY_VALUE_BUCKET = 'il_bt_value_fk_bucket';
34 private const FOREIGN_KEY_VALUE_PARENT_TASK = 'il_bt_value_fk_parent_task';
35 private const FOREIGN_KEY_VALUE_TO_TASK_TASK = 'il_bt_value_to_task_fk_task';
36 private const FOREIGN_KEY_VALUE_TO_TASK_VALUE = 'il_bt_value_to_task_fk_value';
37 private const FOREIGN_KEY_VALUE_TO_TASK_BUCKET = 'il_bt_value_to_task_fk_bucket';
38
39 protected ilDBInterface $db;
40
41 public function prepare(ilDBInterface $db): void
42 {
43 $this->db = $db;
44 }
45
46 public function step_1(): void
47 {
48 $index_exists_user_id_state = $this->db->indexExistsByFields(self::TABLE_BUCKET, ['user_id', 'state']);
49 if (
50 $this->db->indexExistsByFields(self::TABLE_BUCKET, ['user_id'])
51 && !$index_exists_user_id_state
52 ) {
53 $this->db->dropIndexByFields(self::TABLE_BUCKET, ['user_id']);
54 }
55
56 if (!$index_exists_user_id_state) {
57 $this->db->addIndex(self::TABLE_BUCKET, ['user_id', 'state'], 'i1');
58 }
59
60 if (!$this->db->indexExistsByFields(self::TABLE_TASK, ['bucket_id'])) {
61 $this->db->addIndex(self::TABLE_TASK, ['bucket_id'], 'i1');
62 }
63
64 if (!$this->db->indexExistsByFields(self::TABLE_VALUE_TO_TASK, ['bucket_id'])) {
65 $this->db->addIndex(self::TABLE_VALUE_TO_TASK, ['bucket_id'], 'i3');
66 }
67 }
68
69 public function step_2(): void
70 {
71 $tables = [
76 ];
77
78 if ($tables !== $this->filterInnoDBTables($tables)) {
79 return;
80 }
81
82 $this->removeOrphanedRecords();
83
84 if (!$this->db->foreignKeyExists(self::FOREIGN_KEY_TASK_BUCKET, self::TABLE_TASK)) {
85 $this->db->addForeignKey(
86 self::FOREIGN_KEY_TASK_BUCKET,
87 ['bucket_id'],
88 self::TABLE_TASK,
89 ['id'],
90 self::TABLE_BUCKET,
91 null,
93 );
94 }
95
96 if (!$this->db->foreignKeyExists(self::FOREIGN_KEY_VALUE_BUCKET, self::TABLE_VALUE)) {
97 $this->db->addForeignKey(
98 self::FOREIGN_KEY_VALUE_BUCKET,
99 ['bucket_id'],
100 self::TABLE_VALUE,
101 ['id'],
102 self::TABLE_BUCKET,
103 null,
105 );
106 }
107
108 if (!$this->db->foreignKeyExists(self::FOREIGN_KEY_VALUE_PARENT_TASK, self::TABLE_VALUE)) {
109 $this->db->addForeignKey(
110 self::FOREIGN_KEY_VALUE_PARENT_TASK,
111 ['parent_task_id'],
112 self::TABLE_VALUE,
113 ['id'],
114 self::TABLE_TASK,
115 null,
117 );
118 }
119
120 if (!$this->db->foreignKeyExists(self::FOREIGN_KEY_VALUE_TO_TASK_TASK, self::TABLE_VALUE_TO_TASK)) {
121 $this->db->addForeignKey(
122 self::FOREIGN_KEY_VALUE_TO_TASK_TASK,
123 ['task_id'],
124 self::TABLE_VALUE_TO_TASK,
125 ['id'],
126 self::TABLE_TASK,
127 null,
129 );
130 }
131
132 if (!$this->db->foreignKeyExists(self::FOREIGN_KEY_VALUE_TO_TASK_VALUE, self::TABLE_VALUE_TO_TASK)) {
133 $this->db->addForeignKey(
134 self::FOREIGN_KEY_VALUE_TO_TASK_VALUE,
135 ['value_id'],
136 self::TABLE_VALUE_TO_TASK,
137 ['id'],
138 self::TABLE_VALUE,
139 null,
141 );
142 }
143
144 if (!$this->db->foreignKeyExists(self::FOREIGN_KEY_VALUE_TO_TASK_BUCKET, self::TABLE_VALUE_TO_TASK)) {
145 $this->db->addForeignKey(
146 self::FOREIGN_KEY_VALUE_TO_TASK_BUCKET,
147 ['bucket_id'],
148 self::TABLE_VALUE_TO_TASK,
149 ['id'],
150 self::TABLE_BUCKET,
151 null,
153 );
154 }
155
156 if (!$this->db->foreignKeyExists(self::FOREIGN_KEY_BUCKET_ROOT_TASK, self::TABLE_BUCKET)) {
157 $this->db->addForeignKey(
158 self::FOREIGN_KEY_BUCKET_ROOT_TASK,
159 ['root_task_id'],
160 self::TABLE_BUCKET,
161 ['id'],
162 self::TABLE_TASK,
163 null,
164 ForeignKeyConstraints::SET_NULL
165 );
166 }
167
168 if (!$this->db->foreignKeyExists(self::FOREIGN_KEY_BUCKET_CURRENT_TASK, self::TABLE_BUCKET)) {
169 $this->db->addForeignKey(
170 self::FOREIGN_KEY_BUCKET_CURRENT_TASK,
171 ['current_task_id'],
172 self::TABLE_BUCKET,
173 ['id'],
174 self::TABLE_TASK,
175 null,
176 ForeignKeyConstraints::SET_NULL
177 );
178 }
179 }
180
181 private function removeOrphanedRecords(): void
182 {
183 if ($this->db->tableExists(self::TABLE_VALUE_TO_TASK)) {
184 $this->db->manipulate(
185 'DELETE vt FROM ' . self::TABLE_VALUE_TO_TASK . ' vt'
186 . ' LEFT JOIN ' . self::TABLE_BUCKET . ' b ON b.id = vt.bucket_id'
187 . ' LEFT JOIN ' . self::TABLE_TASK . ' t ON t.id = vt.task_id'
188 . ' LEFT JOIN ' . self::TABLE_VALUE . ' v ON v.id = vt.value_id'
189 . ' WHERE b.id IS NULL OR t.id IS NULL OR v.id IS NULL'
190 );
191 }
192
193 if ($this->db->tableExists(self::TABLE_VALUE)) {
194 $this->db->manipulate(
195 'UPDATE ' . self::TABLE_VALUE
196 . ' SET parent_task_id = NULL'
197 . ' WHERE parent_task_id = 0 OR has_parent_task = 0'
198 );
199
200 $this->db->manipulate(
201 'DELETE v FROM ' . self::TABLE_VALUE . ' v'
202 . ' LEFT JOIN ' . self::TABLE_BUCKET . ' b ON b.id = v.bucket_id'
203 . ' WHERE b.id IS NULL'
204 );
205
206 $this->db->manipulate(
207 'DELETE v FROM ' . self::TABLE_VALUE . ' v'
208 . ' LEFT JOIN ' . self::TABLE_TASK . ' t ON t.id = v.parent_task_id'
209 . ' WHERE v.parent_task_id IS NOT NULL AND t.id IS NULL'
210 );
211 }
212
213 if ($this->db->tableExists(self::TABLE_TASK)) {
214 $this->db->manipulate(
215 'DELETE t FROM ' . self::TABLE_TASK . ' t'
216 . ' LEFT JOIN ' . self::TABLE_BUCKET . ' b ON b.id = t.bucket_id'
217 . ' WHERE b.id IS NULL'
218 );
219 }
220
221 if ($this->db->tableExists(self::TABLE_BUCKET)) {
222 $this->db->manipulate(
223 'UPDATE ' . self::TABLE_BUCKET . ' b'
224 . ' LEFT JOIN ' . self::TABLE_TASK . ' rt ON rt.id = b.root_task_id'
225 . ' SET b.root_task_id = NULL'
226 . ' WHERE b.root_task_id IS NOT NULL AND b.root_task_id != 0 AND rt.id IS NULL'
227 );
228
229 $this->db->manipulate(
230 'UPDATE ' . self::TABLE_BUCKET . ' b'
231 . ' LEFT JOIN ' . self::TABLE_TASK . ' ct ON ct.id = b.current_task_id'
232 . ' SET b.current_task_id = NULL'
233 . ' WHERE b.current_task_id IS NOT NULL AND b.current_task_id != 0 AND ct.id IS NULL'
234 );
235 }
236 }
237
242 private function filterInnoDBTables(array $tables): array
243 {
244 $innodb_tables = [];
245
246 foreach ($tables as $table) {
247 if (!$this->db->tableExists($table)) {
248 continue;
249 }
250
251 $result = $this->db->queryF(
252 'SELECT ENGINE FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s',
254 [$table]
255 );
256 $row = $this->db->fetchAssoc($result);
257
258 if (strcasecmp((string) ($row['ENGINE'] ?? ''), ilDBConstants::MYSQL_ENGINE_INNODB) === 0) {
259 $innodb_tables[] = $table;
260 }
261 }
262
263 return $innodb_tables;
264 }
265}
const CASCADE
Definition: Title.php:33
const MYSQL_ENGINE_INNODB
Interface ilDBInterface.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...