Fix constraint checking and only run it if not already constrained

This commit is contained in:
Mikael Nordfeldth 2016-04-01 06:24:11 +02:00
parent 558cbe5b6d
commit 195285ac2f

View File

@ -3087,68 +3087,77 @@ class Notice extends Managed_DataObject
$schema = Schema::get(); $schema = Schema::get();
$schemadef = $schema->getTableDef($table); $schemadef = $schema->getTableDef($table);
printfnq("\nConstraint checking Notice table...\n");
/** /**
* Improve typing and make sure no NULL values in any id-related columns are 0 * Make sure constraints are met before upgrading, if foreign keys
* are not already in use.
* 2016-03-31 * 2016-03-31
*/ */
foreach (['reply_to', 'repeat_of'] as $field) { if (!isset($schemadef['foreign keys'])) {
$notice = new Notice(); // reset the object $newschemadef = self::schemaDef();
$notice->query(sprintf('UPDATE %1$s SET %2$s=NULL WHERE %2$s=0', $notice->escapedTableName(), $field)); printfnq("\nConstraint checking Notice table...\n");
// Now we're sure that no Notice entries have repeat_of=0, only an id > 0 or NULL /**
unset($notice); * Improve typing and make sure no NULL values in any id-related columns are 0
} * 2016-03-31
*/
/** foreach (['reply_to', 'repeat_of'] as $field) {
* Make sure constraints are met before upgrading. $notice = new Notice(); // reset the object
* 2016-03-31 $notice->query(sprintf('UPDATE %1$s SET %2$s=NULL WHERE %2$s=0', $notice->escapedTableName(), $field));
* // Now we're sure that no Notice entries have repeat_of=0, only an id > 0 or NULL
* Will find foreign keys which do not fulfill the constraints and fix unset($notice);
* where appropriate, such as delete when "repeat_of" ID not found in notice.id }
* or set to NULL for "reply_to" in the same case.
* /**
* XXX: How does this work if we would use multicolumn foreign keys? * This Will find foreign keys which do not fulfill the constraints and fix
*/ * where appropriate, such as delete when "repeat_of" ID not found in notice.id
foreach (['reply_to' => 'reset', 'repeat_of' => 'delete', 'profile' => 'delete'] as $field=>$action) { * or set to NULL for "reply_to" in the same case.
$notice = new Notice(); * 2016-03-31
*
$fkeyname = $notice->tableName().'_'.$field.'_fkey'; * XXX: How does this work if we would use multicolumn foreign keys?
assert(isset($schemadef['foreign keys'][$fkeyname]) && $schemadef['foreign keys'][$fkeyname]); */
$foreign_key = $schemadef['foreign keys'][$fkeyname]; foreach (['reply_to' => 'reset', 'repeat_of' => 'delete', 'profile_id' => 'delete'] as $field=>$action) {
printfnq("\n"._ve($schemadef)); $notice = new Notice();
$fkeytable = $foreign_key[0];
assert(isset($foreign_key[1][$field])); $fkeyname = $notice->tableName().'_'.$field.'_fkey';
$fkeycol = $foreign_key[1][$field]; assert(isset($newschemadef['foreign keys'][$fkeyname]));
assert($newschemadef['foreign keys'][$fkeyname]);
// NOTE: Above we set all repeat_of to NULL if they were 0, so this really gets them all.
$notice->whereAdd(sprintf('%1$s NOT IN (SELECT %2$s FROM %3$s)', $field, $fkeycol, $fkeytable)); $foreign_key = $newschemadef['foreign keys'][$fkeyname];
if ($notice->find()) { $fkeytable = $foreign_key[0];
printfnq("\tFound {$notice->N} notices with {$field} NOT IN notice.id, {$action}ing..."); assert(isset($foreign_key[1][$field]));
switch ($action) { $fkeycol = $foreign_key[1][$field];
case 'delete': // since it's a directly dependant notice for an unknown ID we don't want it in our DB
while ($notice->fetch()) { printfnq("* {$fkeyname} ({$field} => {$fkeytable}.{$fkeycol})\n");
// $notice->delete();
printfnq("\n deleting {$notice->id}"); // NOTE: Above we set all repeat_of to NULL if they were 0, so this really gets them all.
} $notice->whereAdd(sprintf('%1$s NOT IN (SELECT %2$s FROM %3$s)', $field, $fkeycol, $fkeytable));
break; if ($notice->find()) {
case 'reset': // just set it to NULL to be compatible with our constraints, if it was related to an unknown ID printfnq("\tFound {$notice->N} notices with {$field} NOT IN notice.id, {$action}ing...");
$ids = []; switch ($action) {
foreach ($notice->fetchAll('id') as $id) { case 'delete': // since it's a directly dependant notice for an unknown ID we don't want it in our DB
settype($id, 'int'); while ($notice->fetch()) {
$ids[] = $id; $notice->delete();
} }
$notice = new Notice(); break;
$notice->query(sprintf('UPDATE %1$s SET %2$s=NULL WHERE id IN (%3$s)', case 'reset': // just set it to NULL to be compatible with our constraints, if it was related to an unknown ID
$notice->escapedTableName(), $ids = [];
$field, foreach ($notice->fetchAll('id') as $id) {
implode(',', $ids))); settype($id, 'int');
break; $ids[] = $id;
default: }
throw new ServerException('The programmer sucks, invalid action name when fixing table.'); unset($notice);
} $notice = new Notice();
printfnq("DONE.\n"); $notice->query(sprintf('UPDATE %1$s SET %2$s=NULL WHERE id IN (%3$s)',
$notice->escapedTableName(),
$field,
implode(',', $ids)));
break;
default:
throw new ServerException('The programmer sucks, invalid action name when fixing table.');
}
printfnq("DONE.\n");
}
unset($notice);
} }
unset($notice);
} }
// 2015-09-04 We move Notice location data to Notice_location // 2015-09-04 We move Notice location data to Notice_location