Differential D10771 Diff 25872 src/infrastructure/storage/management/workflow/PhabricatorStorageManagementAdjustWorkflow.php
Changeset View
Changeset View
Standalone View
Standalone View
src/infrastructure/storage/management/workflow/PhabricatorStorageManagementAdjustWorkflow.php
Show First 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | final class PhabricatorStorageManagementAdjustWorkflow | ||||
private function adjustSchemata($force, $unsafe) { | private function adjustSchemata($force, $unsafe) { | ||||
$console = PhutilConsole::getConsole(); | $console = PhutilConsole::getConsole(); | ||||
$console->writeOut( | $console->writeOut( | ||||
"%s\n", | "%s\n", | ||||
pht('Verifying database schemata...')); | pht('Verifying database schemata...')); | ||||
$adjustments = $this->findAdjustments(); | list($adjustments, $errors) = $this->findAdjustments(); | ||||
$api = $this->getAPI(); | $api = $this->getAPI(); | ||||
if (!$adjustments) { | if (!$adjustments) { | ||||
$console->writeOut( | $console->writeOut( | ||||
"%s\n", | "%s\n", | ||||
pht('Found no issues with schemata.')); | pht('Found no adjustments for schemata.')); | ||||
return; | |||||
return $this->printErrors($errors, 0); | |||||
} | } | ||||
if (!$force && !$api->isCharacterSetAvailable('utf8mb4')) { | if (!$force && !$api->isCharacterSetAvailable('utf8mb4')) { | ||||
$message = pht( | $message = pht( | ||||
"You have an old version of MySQL (older than 5.5) which does not ". | "You have an old version of MySQL (older than 5.5) which does not ". | ||||
"support the utf8mb4 character set. If you apply adjustments now ". | "support the utf8mb4 character set. If you apply adjustments now ". | ||||
"and later update MySQL to 5.5 or newer, you'll need to apply ". | "and later update MySQL to 5.5 or newer, you'll need to apply ". | ||||
"adjustments again (and they will take a long time).\n\n". | "adjustments again (and they will take a long time).\n\n". | ||||
▲ Show 20 Lines • Show All 241 Lines • ▼ Show 20 Lines | foreach ($phases as $phase) { | ||||
} | } | ||||
} | } | ||||
$bar->done(); | $bar->done(); | ||||
if (!$failed) { | if (!$failed) { | ||||
$console->writeOut( | $console->writeOut( | ||||
"%s\n", | "%s\n", | ||||
pht('Completed fixing all schema issues.')); | pht('Completed fixing all schema issues.')); | ||||
return 0; | |||||
} | |||||
$err = 0; | |||||
} else { | |||||
$table = id(new PhutilConsoleTable()) | $table = id(new PhutilConsoleTable()) | ||||
->addColumn('target', array('title' => pht('Target'))) | ->addColumn('target', array('title' => pht('Target'))) | ||||
->addColumn('error', array('title' => pht('Error'))); | ->addColumn('error', array('title' => pht('Error'))); | ||||
foreach ($failed as $failure) { | foreach ($failed as $failure) { | ||||
list($adjust, $ex) = $failure; | list($adjust, $ex) = $failure; | ||||
$pieces = array_select_keys($adjust, array('database', 'table', 'name')); | $pieces = array_select_keys( | ||||
$adjust, | |||||
array('database', 'table', 'name')); | |||||
$pieces = array_filter($pieces); | $pieces = array_filter($pieces); | ||||
$target = implode('.', $pieces); | $target = implode('.', $pieces); | ||||
$table->addRow( | $table->addRow( | ||||
array( | array( | ||||
'target' => $target, | 'target' => $target, | ||||
'error' => $ex->getMessage(), | 'error' => $ex->getMessage(), | ||||
)); | )); | ||||
} | } | ||||
$console->writeOut("\n"); | $console->writeOut("\n"); | ||||
$table->draw(); | $table->draw(); | ||||
$console->writeOut( | $console->writeOut( | ||||
"\n%s\n", | "\n%s\n", | ||||
pht('Failed to make some schema adjustments, detailed above.')); | pht('Failed to make some schema adjustments, detailed above.')); | ||||
$console->writeOut( | $console->writeOut( | ||||
"%s\n", | "%s\n", | ||||
pht( | pht( | ||||
'For help troubleshooting adjustments, see "Managing Storage '. | 'For help troubleshooting adjustments, see "Managing Storage '. | ||||
'Adjustments" in the documentation.')); | 'Adjustments" in the documentation.')); | ||||
return 1; | $err = 1; | ||||
} | |||||
return $this->printErrors($errors, $err); | |||||
} | } | ||||
private function findAdjustments() { | private function findAdjustments() { | ||||
list($comp, $expect, $actual) = $this->loadSchemata(); | list($comp, $expect, $actual) = $this->loadSchemata(); | ||||
$issue_charset = PhabricatorConfigStorageSchema::ISSUE_CHARSET; | $issue_charset = PhabricatorConfigStorageSchema::ISSUE_CHARSET; | ||||
$issue_collation = PhabricatorConfigStorageSchema::ISSUE_COLLATION; | $issue_collation = PhabricatorConfigStorageSchema::ISSUE_COLLATION; | ||||
$issue_columntype = PhabricatorConfigStorageSchema::ISSUE_COLUMNTYPE; | $issue_columntype = PhabricatorConfigStorageSchema::ISSUE_COLUMNTYPE; | ||||
$issue_surpluskey = PhabricatorConfigStorageSchema::ISSUE_SURPLUSKEY; | $issue_surpluskey = PhabricatorConfigStorageSchema::ISSUE_SURPLUSKEY; | ||||
$issue_missingkey = PhabricatorConfigStorageSchema::ISSUE_MISSINGKEY; | $issue_missingkey = PhabricatorConfigStorageSchema::ISSUE_MISSINGKEY; | ||||
$issue_columns = PhabricatorConfigStorageSchema::ISSUE_KEYCOLUMNS; | $issue_columns = PhabricatorConfigStorageSchema::ISSUE_KEYCOLUMNS; | ||||
$issue_unique = PhabricatorConfigStorageSchema::ISSUE_UNIQUE; | $issue_unique = PhabricatorConfigStorageSchema::ISSUE_UNIQUE; | ||||
$issue_longkey = PhabricatorConfigStorageSchema::ISSUE_LONGKEY; | $issue_longkey = PhabricatorConfigStorageSchema::ISSUE_LONGKEY; | ||||
$issue_auto = PhabricatorConfigStorageSchema::ISSUE_AUTOINCREMENT; | $issue_auto = PhabricatorConfigStorageSchema::ISSUE_AUTOINCREMENT; | ||||
$adjustments = array(); | $adjustments = array(); | ||||
$errors = array(); | |||||
foreach ($comp->getDatabases() as $database_name => $database) { | foreach ($comp->getDatabases() as $database_name => $database) { | ||||
foreach ($this->findErrors($database) as $issue) { | |||||
$errors[] = array( | |||||
'database' => $database_name, | |||||
'issue' => $issue, | |||||
); | |||||
} | |||||
$expect_database = $expect->getDatabase($database_name); | $expect_database = $expect->getDatabase($database_name); | ||||
$actual_database = $actual->getDatabase($database_name); | $actual_database = $actual->getDatabase($database_name); | ||||
if (!$expect_database || !$actual_database) { | if (!$expect_database || !$actual_database) { | ||||
// If there's a real issue here, skip this stuff. | // If there's a real issue here, skip this stuff. | ||||
continue; | continue; | ||||
} | } | ||||
Show All 11 Lines | foreach ($comp->getDatabases() as $database_name => $database) { | ||||
'database' => $database_name, | 'database' => $database_name, | ||||
'issues' => $issues, | 'issues' => $issues, | ||||
'charset' => $expect_database->getCharacterSet(), | 'charset' => $expect_database->getCharacterSet(), | ||||
'collation' => $expect_database->getCollation(), | 'collation' => $expect_database->getCollation(), | ||||
); | ); | ||||
} | } | ||||
foreach ($database->getTables() as $table_name => $table) { | foreach ($database->getTables() as $table_name => $table) { | ||||
foreach ($this->findErrors($table) as $issue) { | |||||
$errors[] = array( | |||||
'database' => $database_name, | |||||
'table' => $table_name, | |||||
'issue' => $issue, | |||||
); | |||||
} | |||||
$expect_table = $expect_database->getTable($table_name); | $expect_table = $expect_database->getTable($table_name); | ||||
$actual_table = $actual_database->getTable($table_name); | $actual_table = $actual_database->getTable($table_name); | ||||
if (!$expect_table || !$actual_table) { | if (!$expect_table || !$actual_table) { | ||||
continue; | continue; | ||||
} | } | ||||
$issues = array(); | $issues = array(); | ||||
if ($table->hasIssue($issue_collation)) { | if ($table->hasIssue($issue_collation)) { | ||||
$issues[] = $issue_collation; | $issues[] = $issue_collation; | ||||
} | } | ||||
if ($issues) { | if ($issues) { | ||||
$adjustments[] = array( | $adjustments[] = array( | ||||
'kind' => 'table', | 'kind' => 'table', | ||||
'database' => $database_name, | 'database' => $database_name, | ||||
'table' => $table_name, | 'table' => $table_name, | ||||
'issues' => $issues, | 'issues' => $issues, | ||||
'collation' => $expect_table->getCollation(), | 'collation' => $expect_table->getCollation(), | ||||
); | ); | ||||
} | } | ||||
foreach ($table->getColumns() as $column_name => $column) { | foreach ($table->getColumns() as $column_name => $column) { | ||||
foreach ($this->findErrors($column) as $issue) { | |||||
$errors[] = array( | |||||
'database' => $database_name, | |||||
'table' => $table_name, | |||||
'name' => $column_name, | |||||
'issue' => $issue, | |||||
); | |||||
} | |||||
$expect_column = $expect_table->getColumn($column_name); | $expect_column = $expect_table->getColumn($column_name); | ||||
$actual_column = $actual_table->getColumn($column_name); | $actual_column = $actual_table->getColumn($column_name); | ||||
if (!$expect_column || !$actual_column) { | if (!$expect_column || !$actual_column) { | ||||
continue; | continue; | ||||
} | } | ||||
$issues = array(); | $issues = array(); | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | foreach ($comp->getDatabases() as $database_name => $database) { | ||||
$adjustment['auto'] = $expect_column->getAutoIncrement(); | $adjustment['auto'] = $expect_column->getAutoIncrement(); | ||||
} | } | ||||
$adjustments[] = $adjustment; | $adjustments[] = $adjustment; | ||||
} | } | ||||
} | } | ||||
foreach ($table->getKeys() as $key_name => $key) { | foreach ($table->getKeys() as $key_name => $key) { | ||||
foreach ($this->findErrors($key) as $issue) { | |||||
$errors[] = array( | |||||
'database' => $database_name, | |||||
'table' => $table_name, | |||||
'name' => $key_name, | |||||
'issue' => $issue, | |||||
); | |||||
} | |||||
$expect_key = $expect_table->getKey($key_name); | $expect_key = $expect_table->getKey($key_name); | ||||
$actual_key = $actual_table->getKey($key_name); | $actual_key = $actual_table->getKey($key_name); | ||||
$issues = array(); | $issues = array(); | ||||
$keep_key = true; | $keep_key = true; | ||||
if ($key->hasIssue($issue_surpluskey)) { | if ($key->hasIssue($issue_surpluskey)) { | ||||
$issues[] = $issue_surpluskey; | $issues[] = $issue_surpluskey; | ||||
$keep_key = false; | $keep_key = false; | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | foreach ($comp->getDatabases() as $database_name => $database) { | ||||
} | } | ||||
$adjustments[] = $adjustment; | $adjustments[] = $adjustment; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return $adjustments; | return array($adjustments, $errors); | ||||
} | } | ||||
private function findErrors(PhabricatorConfigStorageSchema $schema) { | |||||
$result = array(); | |||||
foreach ($schema->getLocalIssues() as $issue) { | |||||
$status = PhabricatorConfigStorageSchema::getIssueStatus($issue); | |||||
if ($status == PhabricatorConfigStorageSchema::STATUS_FAIL) { | |||||
$result[] = $issue; | |||||
} | |||||
} | |||||
return $result; | |||||
} | |||||
private function printErrors(array $errors, $default_return) { | |||||
if (!$errors) { | |||||
return $default_return; | |||||
} | |||||
$console = PhutilConsole::getConsole(); | |||||
$table = id(new PhutilConsoleTable()) | |||||
->addColumn('target', array('title' => pht('Target'))) | |||||
->addColumn('error', array('title' => pht('Error'))); | |||||
foreach ($errors as $error) { | |||||
$pieces = array_select_keys( | |||||
$error, | |||||
array('database', 'table', 'name')); | |||||
$pieces = array_filter($pieces); | |||||
$target = implode('.', $pieces); | |||||
$name = PhabricatorConfigStorageSchema::getIssueName($error['issue']); | |||||
$table->addRow( | |||||
array( | |||||
'target' => $target, | |||||
'error' => $name, | |||||
)); | |||||
} | |||||
$console->writeOut("\n"); | |||||
$table->draw(); | |||||
$console->writeOut("\n"); | |||||
$message = pht( | |||||
"The schemata have serious errors (detailed above) which the adjustment ". | |||||
"workflow can not fix.\n\n". | |||||
"If you are not developing Phabricator itself, report this issue to ". | |||||
"the upstream.\n\n". | |||||
"If you are developing Phabricator, these errors usually indicate that ". | |||||
"your schema specifications do not agree with the schemata your code ". | |||||
"actually builds."); | |||||
$console->writeOut( | |||||
"**<bg:red> %s </bg>**\n\n%s\n", | |||||
pht('SCHEMATA ERRORS'), | |||||
phutil_console_wrap($message)); | |||||
return 2; | |||||
} | |||||
} | } |