Completed
Push — master ( 784f76...5704bc )
by Joas
31:00
created
core/Migrations/Version32000Date20250731062008.php 1 patch
Indentation   +120 added lines, -120 removed lines patch added patch discarded remove patch
@@ -22,124 +22,124 @@
 block discarded – undo
22 22
  * The new unique constraint is added in @see \OC\Core\Listener\AddMissingIndicesListener
23 23
  */
24 24
 class Version32000Date20250731062008 extends SimpleMigrationStep {
25
-	public function __construct(
26
-		private IDBConnection $connection,
27
-	) {
28
-	}
29
-
30
-	/**
31
-	 * @param IOutput $output
32
-	 * @param Closure(): ISchemaWrapper $schemaClosure
33
-	 * @param array $options
34
-	 */
35
-	#[Override]
36
-	public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
37
-		// Clean up duplicate categories before adding unique constraint
38
-		$this->cleanupDuplicateCategories($output);
39
-	}
40
-
41
-	/**
42
-	 * Clean up duplicate categories
43
-	 */
44
-	private function cleanupDuplicateCategories(IOutput $output): void {
45
-		$output->info('Starting cleanup of duplicate vcategory records...');
46
-
47
-		// Find all categories, ordered to identify duplicates
48
-		$qb = $this->connection->getQueryBuilder();
49
-		$qb->select('id', 'uid', 'type', 'category')
50
-			->from('vcategory')
51
-			->orderBy('uid')
52
-			->addOrderBy('type')
53
-			->addOrderBy('category')
54
-			->addOrderBy('id');
55
-
56
-		$result = $qb->executeQuery();
57
-
58
-		$seen = [];
59
-		$duplicateCount = 0;
60
-
61
-		while ($category = $result->fetch()) {
62
-			$key = $category['uid'] . '|' . $category['type'] . '|' . $category['category'];
63
-			$categoryId = (int)$category['id'];
64
-
65
-			if (!isset($seen[$key])) {
66
-				// First occurrence - keep this one
67
-				$seen[$key] = $categoryId;
68
-				continue;
69
-			}
70
-
71
-			// Duplicate found
72
-			$keepId = $seen[$key];
73
-			$duplicateCount++;
74
-
75
-			$output->info("Found duplicate: keeping ID $keepId, removing ID $categoryId");
76
-
77
-			$this->cleanupDuplicateAssignments($output, $categoryId, $keepId);
78
-
79
-			// Update object references
80
-			$updateQb = $this->connection->getQueryBuilder();
81
-			$updateQb->update('vcategory_to_object')
82
-				->set('categoryid', $updateQb->createNamedParameter($keepId))
83
-				->where($updateQb->expr()->eq('categoryid', $updateQb->createNamedParameter($categoryId)));
84
-
85
-			$affectedRows = $updateQb->executeStatement();
86
-			if ($affectedRows > 0) {
87
-				$output->info(" - Updated $affectedRows object references from category $categoryId to $keepId");
88
-			}
89
-
90
-			// Remove duplicate category record
91
-			$deleteQb = $this->connection->getQueryBuilder();
92
-			$deleteQb->delete('vcategory')
93
-				->where($deleteQb->expr()->eq('id', $deleteQb->createNamedParameter($categoryId)));
94
-
95
-			$deleteQb->executeStatement();
96
-			$output->info(" - Deleted duplicate category record ID $categoryId");
97
-
98
-		}
99
-
100
-		$result->closeCursor();
101
-
102
-		if ($duplicateCount === 0) {
103
-			$output->info('No duplicate categories found');
104
-		} else {
105
-			$output->info("Duplicate cleanup completed - processed $duplicateCount duplicates");
106
-		}
107
-	}
108
-
109
-	/**
110
-	 * Clean up duplicate assignments
111
-	 * That will delete rows with $categoryId when there is the same row with $keepId
112
-	 */
113
-	private function cleanupDuplicateAssignments(IOutput $output, int $categoryId, int $keepId): void {
114
-		$selectQb = $this->connection->getQueryBuilder();
115
-		$selectQb->select('o1.*')
116
-			->from('vcategory_to_object', 'o1')
117
-			->join(
118
-				'o1', 'vcategory_to_object', 'o2',
119
-				$selectQb->expr()->andX(
120
-					$selectQb->expr()->eq('o1.type', 'o2.type'),
121
-					$selectQb->expr()->eq('o1.objid', 'o2.objid'),
122
-				)
123
-			)
124
-			->where($selectQb->expr()->eq('o1.categoryid', $selectQb->createNamedParameter($categoryId)))
125
-			->andWhere($selectQb->expr()->eq('o2.categoryid', $selectQb->createNamedParameter($keepId)));
126
-
127
-		$deleteQb = $this->connection->getQueryBuilder();
128
-		$deleteQb->delete('vcategory_to_object')
129
-			->where($deleteQb->expr()->eq('objid', $deleteQb->createParameter('objid')))
130
-			->andWhere($deleteQb->expr()->eq('categoryid', $deleteQb->createParameter('categoryid')))
131
-			->andWhere($deleteQb->expr()->eq('type', $deleteQb->createParameter('type')));
132
-
133
-		$duplicatedAssignments = $selectQb->executeQuery();
134
-		$count = 0;
135
-		while ($row = $duplicatedAssignments->fetch()) {
136
-			$deleteQb
137
-				->setParameters($row)
138
-				->executeStatement();
139
-			$count++;
140
-		}
141
-		if ($count > 0) {
142
-			$output->info(" - Deleted $count duplicate category assignments for $categoryId and $keepId");
143
-		}
144
-	}
25
+    public function __construct(
26
+        private IDBConnection $connection,
27
+    ) {
28
+    }
29
+
30
+    /**
31
+     * @param IOutput $output
32
+     * @param Closure(): ISchemaWrapper $schemaClosure
33
+     * @param array $options
34
+     */
35
+    #[Override]
36
+    public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
37
+        // Clean up duplicate categories before adding unique constraint
38
+        $this->cleanupDuplicateCategories($output);
39
+    }
40
+
41
+    /**
42
+     * Clean up duplicate categories
43
+     */
44
+    private function cleanupDuplicateCategories(IOutput $output): void {
45
+        $output->info('Starting cleanup of duplicate vcategory records...');
46
+
47
+        // Find all categories, ordered to identify duplicates
48
+        $qb = $this->connection->getQueryBuilder();
49
+        $qb->select('id', 'uid', 'type', 'category')
50
+            ->from('vcategory')
51
+            ->orderBy('uid')
52
+            ->addOrderBy('type')
53
+            ->addOrderBy('category')
54
+            ->addOrderBy('id');
55
+
56
+        $result = $qb->executeQuery();
57
+
58
+        $seen = [];
59
+        $duplicateCount = 0;
60
+
61
+        while ($category = $result->fetch()) {
62
+            $key = $category['uid'] . '|' . $category['type'] . '|' . $category['category'];
63
+            $categoryId = (int)$category['id'];
64
+
65
+            if (!isset($seen[$key])) {
66
+                // First occurrence - keep this one
67
+                $seen[$key] = $categoryId;
68
+                continue;
69
+            }
70
+
71
+            // Duplicate found
72
+            $keepId = $seen[$key];
73
+            $duplicateCount++;
74
+
75
+            $output->info("Found duplicate: keeping ID $keepId, removing ID $categoryId");
76
+
77
+            $this->cleanupDuplicateAssignments($output, $categoryId, $keepId);
78
+
79
+            // Update object references
80
+            $updateQb = $this->connection->getQueryBuilder();
81
+            $updateQb->update('vcategory_to_object')
82
+                ->set('categoryid', $updateQb->createNamedParameter($keepId))
83
+                ->where($updateQb->expr()->eq('categoryid', $updateQb->createNamedParameter($categoryId)));
84
+
85
+            $affectedRows = $updateQb->executeStatement();
86
+            if ($affectedRows > 0) {
87
+                $output->info(" - Updated $affectedRows object references from category $categoryId to $keepId");
88
+            }
89
+
90
+            // Remove duplicate category record
91
+            $deleteQb = $this->connection->getQueryBuilder();
92
+            $deleteQb->delete('vcategory')
93
+                ->where($deleteQb->expr()->eq('id', $deleteQb->createNamedParameter($categoryId)));
94
+
95
+            $deleteQb->executeStatement();
96
+            $output->info(" - Deleted duplicate category record ID $categoryId");
97
+
98
+        }
99
+
100
+        $result->closeCursor();
101
+
102
+        if ($duplicateCount === 0) {
103
+            $output->info('No duplicate categories found');
104
+        } else {
105
+            $output->info("Duplicate cleanup completed - processed $duplicateCount duplicates");
106
+        }
107
+    }
108
+
109
+    /**
110
+     * Clean up duplicate assignments
111
+     * That will delete rows with $categoryId when there is the same row with $keepId
112
+     */
113
+    private function cleanupDuplicateAssignments(IOutput $output, int $categoryId, int $keepId): void {
114
+        $selectQb = $this->connection->getQueryBuilder();
115
+        $selectQb->select('o1.*')
116
+            ->from('vcategory_to_object', 'o1')
117
+            ->join(
118
+                'o1', 'vcategory_to_object', 'o2',
119
+                $selectQb->expr()->andX(
120
+                    $selectQb->expr()->eq('o1.type', 'o2.type'),
121
+                    $selectQb->expr()->eq('o1.objid', 'o2.objid'),
122
+                )
123
+            )
124
+            ->where($selectQb->expr()->eq('o1.categoryid', $selectQb->createNamedParameter($categoryId)))
125
+            ->andWhere($selectQb->expr()->eq('o2.categoryid', $selectQb->createNamedParameter($keepId)));
126
+
127
+        $deleteQb = $this->connection->getQueryBuilder();
128
+        $deleteQb->delete('vcategory_to_object')
129
+            ->where($deleteQb->expr()->eq('objid', $deleteQb->createParameter('objid')))
130
+            ->andWhere($deleteQb->expr()->eq('categoryid', $deleteQb->createParameter('categoryid')))
131
+            ->andWhere($deleteQb->expr()->eq('type', $deleteQb->createParameter('type')));
132
+
133
+        $duplicatedAssignments = $selectQb->executeQuery();
134
+        $count = 0;
135
+        while ($row = $duplicatedAssignments->fetch()) {
136
+            $deleteQb
137
+                ->setParameters($row)
138
+                ->executeStatement();
139
+            $count++;
140
+        }
141
+        if ($count > 0) {
142
+            $output->info(" - Deleted $count duplicate category assignments for $categoryId and $keepId");
143
+        }
144
+    }
145 145
 }
Please login to merge, or discard this patch.