diff --git a/src/Dev/Command/DbBuild.php b/src/Dev/Command/DbBuild.php index 10e452a97a5..5bdbe7d1835 100644 --- a/src/Dev/Command/DbBuild.php +++ b/src/Dev/Command/DbBuild.php @@ -220,40 +220,42 @@ public function providePermissions(): array protected function updateLegacyClassNameField(string $dataClass, string $fieldName, array $mapping): void { $schema = DataObject::getSchema(); - // Check first to ensure that the class has the specified field to update - if (!$schema->databaseField($dataClass, $fieldName, false)) { - return; - } - - // Load a list of any records that have obsolete class names - $table = $schema->tableName($dataClass); - $currentClassNameList = DB::query("SELECT DISTINCT(\"{$fieldName}\") FROM \"{$table}\"")->column(); - // Get all invalid classes for this field - $invalidClasses = array_intersect($currentClassNameList ?? [], array_keys($mapping ?? [])); - if (!$invalidClasses) { + if (!$schema->databaseField($dataClass, $fieldName, false) || empty($mapping)) { return; } - $numberClasses = count($invalidClasses ?? []); - DB::alteration_message( - "Correcting obsolete {$fieldName} values for {$numberClasses} outdated types", - 'obsolete' - ); - - // Build case assignment based on all intersected legacy classnames $cases = []; $params = []; - foreach ($invalidClasses as $invalidClass) { + $wherePlaceholders = []; + $whereParams = []; + + foreach ($mapping as $oldClass => $newClass) { $cases[] = "WHEN \"{$fieldName}\" = ? THEN ?"; - $params[] = $invalidClass; - $params[] = $mapping[$invalidClass]; + $params[] = $oldClass; + $params[] = $newClass; + + $wherePlaceholders[] = '?'; + $whereParams[] = $oldClass; } + $casesSQL = implode(' ', $cases); + $wherePlaceholders = implode(',', $wherePlaceholders); + $params = array_merge($params, $whereParams); + foreach ($this->getClassTables($dataClass) as $table) { - $casesSQL = implode(' ', $cases); - $sql = "UPDATE \"{$table}\" SET \"{$fieldName}\" = CASE {$casesSQL} ELSE \"{$fieldName}\" END"; + $sql = "UPDATE \"{$table}\" + SET \"{$fieldName}\" = CASE {$casesSQL} ELSE \"{$fieldName}\" END + WHERE \"{$fieldName}\" IN ({$wherePlaceholders})"; + DB::prepared_query($sql, $params); + + if (DB::get_conn()->affectedRows() > 0) { + DB::alteration_message( + "Corrected obsolete {$fieldName} values in {$table}", + 'obsolete' + ); + } } } diff --git a/src/ORM/FieldType/DBEnum.php b/src/ORM/FieldType/DBEnum.php index d30aa457e15..701c023ae5f 100644 --- a/src/ORM/FieldType/DBEnum.php +++ b/src/ORM/FieldType/DBEnum.php @@ -213,9 +213,42 @@ public function getEnumObsolete(): array // Get all enum values $enumValues = $this->getEnum(); - if (DB::get_schema()->hasField($table, $name)) { - $existing = DB::query("SELECT DISTINCT \"{$name}\" FROM \"{$table}\"")->column(); - $enumValues = array_unique(array_merge($enumValues, $existing)); + + if (!DB::get_schema()->hasField($table, $name)) { + DBEnum::$enum_cache[$table][$name] = $enumValues; + return $enumValues; + } + + $currentConstraintValues = DB::get_schema()->enumValuesForField($table, $name); + $removedValues = array_diff($currentConstraintValues, $enumValues); + + if (empty($removedValues)) { + DBEnum::$enum_cache[$table][$name] = $enumValues; + return $enumValues; + } + + $obsoleteValues = []; + foreach ($removedValues as $value) { + $exists = DB::prepared_query( + "SELECT EXISTS( + SELECT 1 FROM \"{$table}\" + WHERE \"{$name}\" = ? + LIMIT 1 + ) as \"_exists\"", + [$value] + )->value(); + + if ($exists) { + $obsoleteValues[] = $value; + } + } + + if (!empty($obsoleteValues)) { + DB::alteration_message( + "Found obsolete {$name} values in {$table}: " . implode(', ', $obsoleteValues), + 'notice' + ); + $enumValues = array_merge($enumValues, $obsoleteValues); } // Cache and return