diff --git a/src/applications/config/schema/PhabricatorConfigColumnSchema.php b/src/applications/config/schema/PhabricatorConfigColumnSchema.php --- a/src/applications/config/schema/PhabricatorConfigColumnSchema.php +++ b/src/applications/config/schema/PhabricatorConfigColumnSchema.php @@ -125,24 +125,30 @@ PhabricatorConfigStorageSchema $expect) { $issues = array(); - if ($this->getCharacterSet() != $expect->getCharacterSet()) { - $issues[] = self::ISSUE_CHARSET; - } - if ($this->getCollation() != $expect->getCollation()) { - $issues[] = self::ISSUE_COLLATION; - } + $type_unknown = PhabricatorConfigSchemaSpec::DATATYPE_UNKNOWN; + if ($expect->getColumnType() == $type_unknown) { + $issues[] = self::ISSUE_UNKNOWN; + } else { + if ($this->getCharacterSet() != $expect->getCharacterSet()) { + $issues[] = self::ISSUE_CHARSET; + } - if ($this->getColumnType() != $expect->getColumnType()) { - $issues[] = self::ISSUE_COLUMNTYPE; - } + if ($this->getCollation() != $expect->getCollation()) { + $issues[] = self::ISSUE_COLLATION; + } - if ($this->getNullable() !== $expect->getNullable()) { - $issues[] = self::ISSUE_NULLABLE; - } + if ($this->getColumnType() != $expect->getColumnType()) { + $issues[] = self::ISSUE_COLUMNTYPE; + } - if ($this->getAutoIncrement() !== $expect->getAutoIncrement()) { - $issues[] = self::ISSUE_AUTOINCREMENT; + if ($this->getNullable() !== $expect->getNullable()) { + $issues[] = self::ISSUE_NULLABLE; + } + + if ($this->getAutoIncrement() !== $expect->getAutoIncrement()) { + $issues[] = self::ISSUE_AUTOINCREMENT; + } } return $issues; diff --git a/src/applications/config/schema/PhabricatorConfigSchemaSpec.php b/src/applications/config/schema/PhabricatorConfigSchemaSpec.php --- a/src/applications/config/schema/PhabricatorConfigSchemaSpec.php +++ b/src/applications/config/schema/PhabricatorConfigSchemaSpec.php @@ -7,6 +7,8 @@ private $utf8BinaryCollation; private $utf8SortingCollation; + const DATATYPE_UNKNOWN = ''; + public function setUTF8SortingCollation($utf8_sorting_collation) { $this->utf8SortingCollation = $utf8_sorting_collation; return $this; @@ -324,9 +326,9 @@ $column_type = 'date'; break; default: - $column_type = pht(''); - $charset = pht(''); - $collation = pht(''); + $column_type = self::DATATYPE_UNKNOWN; + $charset = self::DATATYPE_UNKNOWN; + $collation = self::DATATYPE_UNKNOWN; break; } } diff --git a/src/applications/config/schema/PhabricatorConfigStorageSchema.php b/src/applications/config/schema/PhabricatorConfigStorageSchema.php --- a/src/applications/config/schema/PhabricatorConfigStorageSchema.php +++ b/src/applications/config/schema/PhabricatorConfigStorageSchema.php @@ -16,6 +16,7 @@ const ISSUE_SUBWARN = 'subwarn'; const ISSUE_SUBFAIL = 'subfail'; const ISSUE_AUTOINCREMENT = 'autoincrement'; + const ISSUE_UNKNOWN = 'unknown'; const STATUS_OKAY = 'okay'; const STATUS_WARN = 'warn'; @@ -127,6 +128,8 @@ return pht('Subschemata Have Failures'); case self::ISSUE_AUTOINCREMENT: return pht('Column has Wrong Autoincrement'); + case self::ISSUE_UNKNOWN: + return pht('Column Has No Specification'); default: throw new Exception(pht('Unknown schema issue "%s"!', $issue)); } @@ -162,6 +165,8 @@ return pht('Subschemata have setup failures.'); case self::ISSUE_AUTOINCREMENT: return pht('This column has the wrong autoincrement setting.'); + case self::ISSUE_UNKNOWN: + return pht('This column is missing a type specification.'); default: throw new Exception(pht('Unknown schema issue "%s"!', $issue)); } @@ -173,6 +178,7 @@ case self::ISSUE_SURPLUS: case self::ISSUE_NULLABLE: case self::ISSUE_SUBFAIL: + case self::ISSUE_UNKNOWN: return self::STATUS_FAIL; case self::ISSUE_SUBWARN: case self::ISSUE_COLUMNTYPE: diff --git a/src/infrastructure/storage/lisk/LiskDAO.php b/src/infrastructure/storage/lisk/LiskDAO.php --- a/src/infrastructure/storage/lisk/LiskDAO.php +++ b/src/infrastructure/storage/lisk/LiskDAO.php @@ -1804,7 +1804,7 @@ } // We don't know the type of this column. - $map[$property] = ''; + $map[$property] = PhabricatorConfigSchemaSpec::DATATYPE_UNKNOWN; } return $map; diff --git a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementAdjustWorkflow.php b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementAdjustWorkflow.php --- a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementAdjustWorkflow.php +++ b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementAdjustWorkflow.php @@ -78,14 +78,15 @@ "%s\n", pht('Verifying database schemata...')); - $adjustments = $this->findAdjustments(); + list($adjustments, $errors) = $this->findAdjustments(); $api = $this->getAPI(); if (!$adjustments) { $console->writeOut( "%s\n", - pht('Found no issues with schemata.')); - return; + pht('Found no adjustments for schemata.')); + + return $this->printErrors($errors, 0); } if (!$force && !$api->isCharacterSetAvailable('utf8mb4')) { @@ -343,39 +344,44 @@ $console->writeOut( "%s\n", pht('Completed fixing all schema issues.')); - return 0; - } - $table = id(new PhutilConsoleTable()) - ->addColumn('target', array('title' => pht('Target'))) - ->addColumn('error', array('title' => pht('Error'))); + $err = 0; + } else { + $table = id(new PhutilConsoleTable()) + ->addColumn('target', array('title' => pht('Target'))) + ->addColumn('error', array('title' => pht('Error'))); - foreach ($failed as $failure) { - list($adjust, $ex) = $failure; + foreach ($failed as $failure) { + list($adjust, $ex) = $failure; - $pieces = array_select_keys($adjust, array('database', 'table', 'name')); - $pieces = array_filter($pieces); - $target = implode('.', $pieces); + $pieces = array_select_keys( + $adjust, + array('database', 'table', 'name')); + $pieces = array_filter($pieces); + $target = implode('.', $pieces); - $table->addRow( - array( - 'target' => $target, - 'error' => $ex->getMessage(), - )); - } + $table->addRow( + array( + 'target' => $target, + 'error' => $ex->getMessage(), + )); + } - $console->writeOut("\n"); - $table->draw(); - $console->writeOut( - "\n%s\n", - pht('Failed to make some schema adjustments, detailed above.')); - $console->writeOut( - "%s\n", - pht( - 'For help troubleshooting adjustments, see "Managing Storage '. - 'Adjustments" in the documentation.')); + $console->writeOut("\n"); + $table->draw(); + $console->writeOut( + "\n%s\n", + pht('Failed to make some schema adjustments, detailed above.')); + $console->writeOut( + "%s\n", + pht( + 'For help troubleshooting adjustments, see "Managing Storage '. + 'Adjustments" in the documentation.')); - return 1; + $err = 1; + } + + return $this->printErrors($errors, $err); } private function findAdjustments() { @@ -392,7 +398,15 @@ $issue_auto = PhabricatorConfigStorageSchema::ISSUE_AUTOINCREMENT; $adjustments = array(); + $errors = array(); 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); $actual_database = $actual->getDatabase($database_name); @@ -420,6 +434,14 @@ } 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); $actual_table = $actual_database->getTable($table_name); @@ -443,6 +465,15 @@ } 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); $actual_column = $actual_table->getColumn($column_name); @@ -503,6 +534,15 @@ } 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); $actual_key = $actual_table->getKey($key_name); @@ -560,8 +600,66 @@ } } - 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( + "** %s **\n\n%s\n", + pht('SCHEMATA ERRORS'), + phutil_console_wrap($message)); + + return 2; + } }