protected function compareToSimilarSchema(PhabricatorConfigStorageSchema $expect)
 {
     $issues = array();
     if ($this->getCollation() != $expect->getCollation()) {
         $issues[] = self::ISSUE_COLLATION;
     }
     return $issues;
 }
 protected function compareToSimilarSchema(PhabricatorConfigStorageSchema $expect)
 {
     $issues = array();
     if ($this->getAccessDenied()) {
         $issues[] = self::ISSUE_ACCESSDENIED;
     } else {
         if ($this->getCharacterSet() != $expect->getCharacterSet()) {
             $issues[] = self::ISSUE_CHARSET;
         }
         if ($this->getCollation() != $expect->getCollation()) {
             $issues[] = self::ISSUE_COLLATION;
         }
     }
     return $issues;
 }
 protected function compareToSimilarSchema(PhabricatorConfigStorageSchema $expect)
 {
     $issues = array();
     if ($this->getColumnNames() !== $expect->getColumnNames()) {
         $issues[] = self::ISSUE_KEYCOLUMNS;
     }
     if ($this->getUnique() !== $expect->getUnique()) {
         $issues[] = self::ISSUE_UNIQUE;
     }
     // A fulltext index can be of any length.
     if ($this->getIndexType() != 'FULLTEXT') {
         if ($this->getKeyByteLength() > self::MAX_INNODB_KEY_LENGTH) {
             $issues[] = self::ISSUE_LONGKEY;
         }
     }
     return $issues;
 }
 private function compareSchemata(PhabricatorConfigStorageSchema $expect = null, PhabricatorConfigStorageSchema $actual = null)
 {
     $expect_is_key = $expect instanceof PhabricatorConfigKeySchema;
     $actual_is_key = $actual instanceof PhabricatorConfigKeySchema;
     if ($expect_is_key || $actual_is_key) {
         $missing_issue = PhabricatorConfigStorageSchema::ISSUE_MISSINGKEY;
         $surplus_issue = PhabricatorConfigStorageSchema::ISSUE_SURPLUSKEY;
     } else {
         $missing_issue = PhabricatorConfigStorageSchema::ISSUE_MISSING;
         $surplus_issue = PhabricatorConfigStorageSchema::ISSUE_SURPLUS;
     }
     if (!$expect && !$actual) {
         throw new Exception(pht('Can not compare two missing schemata!'));
     } else {
         if ($expect && !$actual) {
             $issues = array($missing_issue);
         } else {
             if ($actual && !$expect) {
                 $issues = array($surplus_issue);
             } else {
                 $issues = $actual->compareTo($expect);
             }
         }
     }
     return $issues;
 }
 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')));
     $any_surplus = false;
     $all_surplus = true;
     $any_access = false;
     $all_access = true;
     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']);
         $issue = $error['issue'];
         if ($issue === PhabricatorConfigStorageSchema::ISSUE_SURPLUS) {
             $any_surplus = true;
         } else {
             $all_surplus = false;
         }
         if ($issue === PhabricatorConfigStorageSchema::ISSUE_ACCESSDENIED) {
             $any_access = true;
         } else {
             $all_access = false;
         }
         $table->addRow(array('target' => $target, 'error' => $name));
     }
     $console->writeOut("\n");
     $table->draw();
     $console->writeOut("\n");
     $message = array();
     if ($all_surplus) {
         $message[] = pht('You have surplus schemata (extra tables or columns which Phabricator ' . 'does not expect). For information on resolving these ' . 'issues, see the "Surplus Schemata" section in the "Managing Storage ' . 'Adjustments" article in the documentation.');
     } else {
         if ($all_access) {
             $message[] = pht('The user you are connecting to MySQL with does not have the correct ' . 'permissions, and can not access some databases or tables that it ' . 'needs to be able to access. GRANT the user additional permissions.');
         } else {
             $message[] = pht('The schemata have errors (detailed above) which the adjustment ' . 'workflow can not fix.');
             if ($any_access) {
                 $message[] = pht('Some of these errors are caused by access control problems. ' . 'The user you are connecting with does not have permission to see ' . 'all of the database or tables that Phabricator uses. You need to ' . 'GRANT the user more permission, or use a different user.');
             }
             if ($any_surplus) {
                 $message[] = pht('Some of these errors are caused by surplus schemata (extra ' . 'tables or columns which Phabricator does not expect). These are ' . 'not serious. For information on resolving these issues, see the ' . '"Surplus Schemata" section in the "Managing Storage Adjustments" ' . 'article in the documentation.');
             }
             $message[] = pht('If you are not developing Phabricator itself, report this issue to ' . 'the upstream.');
             $message[] = pht('If you are developing Phabricator, these errors usually indicate ' . 'that your schema specifications do not agree with the schemata your ' . 'code actually builds.');
         }
     }
     $message = implode("\n\n", $message);
     if ($all_surplus) {
         $console->writeOut("**<bg:yellow> %s </bg>**\n\n%s\n", pht('SURPLUS SCHEMATA'), phutil_console_wrap($message));
     } else {
         if ($all_access) {
             $console->writeOut("**<bg:yellow> %s </bg>**\n\n%s\n", pht('ACCESS DENIED'), phutil_console_wrap($message));
         } else {
             $console->writeOut("**<bg:red> %s </bg>**\n\n%s\n", pht('SCHEMATA ERRORS'), phutil_console_wrap($message));
         }
     }
     return 2;
 }
 private function buildProperties(array $properties, array $issues)
 {
     $view = id(new PHUIPropertyListView())->setUser($this->getRequest()->getUser());
     foreach ($properties as $property) {
         list($key, $value) = $property;
         $view->addProperty($key, $value);
     }
     $status_view = new PHUIStatusListView();
     if (!$issues) {
         $status_view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('No Schema Issues')));
     } else {
         foreach ($issues as $issue) {
             $note = PhabricatorConfigStorageSchema::getIssueDescription($issue);
             $status = PhabricatorConfigStorageSchema::getIssueStatus($issue);
             switch ($status) {
                 case PhabricatorConfigStorageSchema::STATUS_WARN:
                     $icon = PHUIStatusItemView::ICON_WARNING;
                     $color = 'yellow';
                     break;
                 case PhabricatorConfigStorageSchema::STATUS_FAIL:
                 default:
                     $icon = PHUIStatusItemView::ICON_REJECT;
                     $color = 'red';
                     break;
             }
             $item = id(new PHUIStatusItemView())->setTarget(PhabricatorConfigStorageSchema::getIssueName($issue))->setIcon($icon, $color)->setNote($note);
             $status_view->addItem($item);
         }
     }
     $view->addProperty(pht('Schema Status'), $status_view);
     return phutil_tag_div('config-page-property', $view);
 }
 protected function compareToSimilarSchema(PhabricatorConfigStorageSchema $expect)
 {
     $issues = array();
     $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->getCollation() != $expect->getCollation()) {
             $issues[] = self::ISSUE_COLLATION;
         }
         if ($this->getColumnType() != $expect->getColumnType()) {
             $issues[] = self::ISSUE_COLUMNTYPE;
         }
         if ($this->getNullable() !== $expect->getNullable()) {
             $issues[] = self::ISSUE_NULLABLE;
         }
         if ($this->getAutoIncrement() !== $expect->getAutoIncrement()) {
             $issues[] = self::ISSUE_AUTOINCREMENT;
         }
     }
     return $issues;
 }
 public function handleRequest(AphrontRequest $request)
 {
     $viewer = $request->getViewer();
     $query = $this->buildSchemaQuery();
     $actual = $query->loadActualSchema();
     $expect = $query->loadExpectedSchema();
     $comp = $query->buildComparisonSchema($expect, $actual);
     $crumbs = $this->buildApplicationCrumbs();
     $crumbs->addTextCrumb(pht('Database Issues'));
     $crumbs->setBorder(true);
     // Collect all open issues.
     $issues = array();
     foreach ($comp->getDatabases() as $database_name => $database) {
         foreach ($database->getLocalIssues() as $issue) {
             $issues[] = array($database_name, null, null, null, $issue);
         }
         foreach ($database->getTables() as $table_name => $table) {
             foreach ($table->getLocalIssues() as $issue) {
                 $issues[] = array($database_name, $table_name, null, null, $issue);
             }
             foreach ($table->getColumns() as $column_name => $column) {
                 foreach ($column->getLocalIssues() as $issue) {
                     $issues[] = array($database_name, $table_name, 'column', $column_name, $issue);
                 }
             }
             foreach ($table->getKeys() as $key_name => $key) {
                 foreach ($key->getLocalIssues() as $issue) {
                     $issues[] = array($database_name, $table_name, 'key', $key_name, $issue);
                 }
             }
         }
     }
     // Sort all open issues so that the most severe issues appear first.
     $order = array();
     $counts = array();
     foreach ($issues as $key => $issue) {
         $const = $issue[4];
         $status = PhabricatorConfigStorageSchema::getIssueStatus($const);
         $severity = PhabricatorConfigStorageSchema::getStatusSeverity($status);
         $order[$key] = sprintf('~%d~%s%s%s', 9 - $severity, $issue[0], $issue[1], $issue[3]);
         if (empty($counts[$status])) {
             $counts[$status] = 0;
         }
         $counts[$status]++;
     }
     asort($order);
     $issues = array_select_keys($issues, array_keys($order));
     // Render the issues.
     $rows = array();
     foreach ($issues as $issue) {
         $const = $issue[4];
         $database_link = phutil_tag('a', array('href' => $this->getApplicationURI('/database/' . $issue[0] . '/')), $issue[0]);
         $rows[] = array($this->renderIcon(PhabricatorConfigStorageSchema::getIssueStatus($const)), $database_link, $issue[1], $issue[2], $issue[3], PhabricatorConfigStorageSchema::getIssueDescription($const));
     }
     $table = id(new AphrontTableView($rows))->setNoDataString(pht('No databases have any issues.'))->setHeaders(array(null, pht('Database'), pht('Table'), pht('Type'), pht('Column/Key'), pht('Issue')))->setColumnClasses(array(null, null, null, null, null, 'wide'));
     $errors = array();
     if (isset($counts[PhabricatorConfigStorageSchema::STATUS_FAIL])) {
         $errors[] = pht('Detected %s serious issue(s) with the schemata.', new PhutilNumber($counts[PhabricatorConfigStorageSchema::STATUS_FAIL]));
     }
     if (isset($counts[PhabricatorConfigStorageSchema::STATUS_WARN])) {
         $errors[] = pht('Detected %s warning(s) with the schemata.', new PhutilNumber($counts[PhabricatorConfigStorageSchema::STATUS_WARN]));
     }
     $title = pht('Database Issues');
     $header = id(new PHUIHeaderView())->setHeader($title)->setProfileHeader(true);
     $nav = $this->buildSideNavView();
     $nav->selectFilter('dbissue/');
     $content = id(new PhabricatorConfigPageView())->setHeader($header)->setContent($table);
     return $this->newPage()->setTitle($title)->setCrumbs($crumbs)->setNavigation($nav)->appendChild($content)->addClass('white-background');
 }