2014-09-18 17:22:18 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class PhabricatorConfigDatabaseController
|
|
|
|
extends PhabricatorConfigController {
|
|
|
|
|
2014-09-18 17:32:21 +02:00
|
|
|
const MAX_INNODB_KEY_LENGTH = 767;
|
|
|
|
|
2014-09-18 17:22:18 +02:00
|
|
|
private $database;
|
|
|
|
private $table;
|
2014-09-18 17:22:54 +02:00
|
|
|
private $column;
|
2014-09-18 17:32:21 +02:00
|
|
|
private $key;
|
2014-09-18 17:22:18 +02:00
|
|
|
|
|
|
|
public function willProcessRequest(array $data) {
|
|
|
|
$this->database = idx($data, 'database');
|
|
|
|
$this->table = idx($data, 'table');
|
2014-09-18 17:22:54 +02:00
|
|
|
$this->column = idx($data, 'column');
|
2014-09-18 17:32:21 +02:00
|
|
|
$this->key = idx($data, 'key');
|
2014-09-18 17:22:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public function processRequest() {
|
|
|
|
$request = $this->getRequest();
|
|
|
|
$viewer = $request->getUser();
|
|
|
|
|
|
|
|
$conf = PhabricatorEnv::newObjectFromConfig(
|
|
|
|
'mysql.configuration-provider',
|
|
|
|
array($dao = null, 'w'));
|
|
|
|
|
|
|
|
$api = id(new PhabricatorStorageManagementAPI())
|
|
|
|
->setUser($conf->getUser())
|
|
|
|
->setHost($conf->getHost())
|
|
|
|
->setPort($conf->getPort())
|
|
|
|
->setNamespace(PhabricatorLiskDAO::getDefaultStorageNamespace())
|
|
|
|
->setPassword($conf->getPassword());
|
|
|
|
|
|
|
|
$query = id(new PhabricatorConfigSchemaQuery())
|
|
|
|
->setAPI($api);
|
|
|
|
|
|
|
|
$actual = $query->loadActualSchema();
|
|
|
|
$expect = $query->loadExpectedSchema();
|
2014-09-18 17:22:54 +02:00
|
|
|
$comp = $query->buildComparisonSchema($expect, $actual);
|
2014-09-18 17:22:18 +02:00
|
|
|
|
2014-09-18 17:22:54 +02:00
|
|
|
if ($this->column) {
|
|
|
|
return $this->renderColumn(
|
|
|
|
$comp,
|
|
|
|
$expect,
|
2014-09-18 17:22:18 +02:00
|
|
|
$actual,
|
2014-09-18 17:22:54 +02:00
|
|
|
$this->database,
|
|
|
|
$this->table,
|
|
|
|
$this->column);
|
2014-09-18 17:32:21 +02:00
|
|
|
} else if ($this->key) {
|
|
|
|
return $this->renderKey(
|
|
|
|
$comp,
|
|
|
|
$expect,
|
|
|
|
$actual,
|
|
|
|
$this->database,
|
|
|
|
$this->table,
|
|
|
|
$this->key);
|
2014-09-18 17:22:54 +02:00
|
|
|
} else if ($this->table) {
|
|
|
|
return $this->renderTable(
|
|
|
|
$comp,
|
2014-09-18 17:22:18 +02:00
|
|
|
$expect,
|
2014-09-18 17:22:54 +02:00
|
|
|
$actual,
|
2014-09-18 17:22:18 +02:00
|
|
|
$this->database,
|
|
|
|
$this->table);
|
|
|
|
} else if ($this->database) {
|
|
|
|
return $this->renderDatabase(
|
2014-09-18 17:22:54 +02:00
|
|
|
$comp,
|
2014-09-18 17:22:18 +02:00
|
|
|
$expect,
|
2014-09-18 17:22:54 +02:00
|
|
|
$actual,
|
2014-09-18 17:22:18 +02:00
|
|
|
$this->database);
|
|
|
|
} else {
|
|
|
|
return $this->renderServer(
|
2014-09-18 17:22:54 +02:00
|
|
|
$comp,
|
|
|
|
$expect,
|
|
|
|
$actual);
|
2014-09-18 17:22:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private function buildResponse($title, $body) {
|
|
|
|
$nav = $this->buildSideNavView();
|
|
|
|
$nav->selectFilter('database/');
|
|
|
|
|
|
|
|
$crumbs = $this->buildApplicationCrumbs();
|
|
|
|
if ($this->database) {
|
|
|
|
$crumbs->addTextCrumb(
|
|
|
|
pht('Database Status'),
|
|
|
|
$this->getApplicationURI('database/'));
|
|
|
|
if ($this->table) {
|
|
|
|
$crumbs->addTextCrumb(
|
|
|
|
$this->database,
|
|
|
|
$this->getApplicationURI('database/'.$this->database.'/'));
|
2014-09-18 17:32:21 +02:00
|
|
|
if ($this->column || $this->key) {
|
2014-09-18 17:25:34 +02:00
|
|
|
$crumbs->addTextCrumb(
|
|
|
|
$this->table,
|
|
|
|
$this->getApplicationURI(
|
|
|
|
'database/'.$this->database.'/'.$this->table.'/'));
|
2014-09-18 17:32:21 +02:00
|
|
|
if ($this->column) {
|
|
|
|
$crumbs->addTextCrumb($this->column);
|
|
|
|
} else {
|
|
|
|
$crumbs->addTextCrumb($this->key);
|
|
|
|
}
|
2014-09-18 17:25:34 +02:00
|
|
|
} else {
|
|
|
|
$crumbs->addTextCrumb($this->table);
|
|
|
|
}
|
2014-09-18 17:22:18 +02:00
|
|
|
} else {
|
|
|
|
$crumbs->addTextCrumb($this->database);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$crumbs->addTextCrumb(pht('Database Status'));
|
|
|
|
}
|
|
|
|
|
|
|
|
$nav->setCrumbs($crumbs);
|
|
|
|
$nav->appendChild($body);
|
|
|
|
|
|
|
|
return $this->buildApplicationPage(
|
|
|
|
$nav,
|
|
|
|
array(
|
|
|
|
'title' => $title,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private function renderServer(
|
2014-09-18 17:22:54 +02:00
|
|
|
PhabricatorConfigServerSchema $comp,
|
|
|
|
PhabricatorConfigServerSchema $expect,
|
|
|
|
PhabricatorConfigServerSchema $actual) {
|
2014-09-18 17:22:18 +02:00
|
|
|
|
2014-09-18 17:22:54 +02:00
|
|
|
$charset_issue = PhabricatorConfigStorageSchema::ISSUE_CHARSET;
|
|
|
|
$collation_issue = PhabricatorConfigStorageSchema::ISSUE_COLLATION;
|
2014-09-18 17:22:18 +02:00
|
|
|
|
|
|
|
$rows = array();
|
2014-09-18 17:22:54 +02:00
|
|
|
foreach ($comp->getDatabases() as $database_name => $database) {
|
|
|
|
$actual_database = $actual->getDatabase($database_name);
|
|
|
|
if ($actual_database) {
|
|
|
|
$charset = $actual_database->getCharacterSet();
|
|
|
|
$collation = $actual_database->getCollation();
|
2014-09-18 17:22:18 +02:00
|
|
|
} else {
|
2014-09-18 17:22:54 +02:00
|
|
|
$charset = null;
|
|
|
|
$collation = null;
|
2014-09-18 17:22:18 +02:00
|
|
|
}
|
|
|
|
|
2014-09-18 17:22:54 +02:00
|
|
|
$status = $database->getStatus();
|
|
|
|
$issues = $database->getIssues();
|
2014-09-18 17:22:18 +02:00
|
|
|
|
|
|
|
$rows[] = array(
|
2014-09-18 17:22:54 +02:00
|
|
|
$this->renderIcon($status),
|
2014-09-18 17:22:18 +02:00
|
|
|
phutil_tag(
|
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => $this->getApplicationURI(
|
|
|
|
'/database/'.$database_name.'/'),
|
|
|
|
),
|
|
|
|
$database_name),
|
2014-09-18 17:22:54 +02:00
|
|
|
$this->renderAttr($charset, $database->hasIssue($charset_issue)),
|
|
|
|
$this->renderAttr($collation, $database->hasIssue($collation_issue)),
|
2014-09-18 17:22:18 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
$table = id(new AphrontTableView($rows))
|
|
|
|
->setHeaders(
|
|
|
|
array(
|
|
|
|
null,
|
|
|
|
pht('Database'),
|
|
|
|
pht('Charset'),
|
|
|
|
pht('Collation'),
|
|
|
|
))
|
|
|
|
->setColumnClasses(
|
|
|
|
array(
|
2014-09-18 17:22:54 +02:00
|
|
|
null,
|
2014-09-18 17:22:18 +02:00
|
|
|
'wide pri',
|
|
|
|
null,
|
|
|
|
null,
|
|
|
|
));
|
|
|
|
|
|
|
|
$title = pht('Database Status');
|
|
|
|
|
2014-09-18 17:22:54 +02:00
|
|
|
$properties = $this->buildProperties(
|
|
|
|
array(
|
|
|
|
),
|
|
|
|
$comp->getIssues());
|
|
|
|
|
2014-09-18 17:22:18 +02:00
|
|
|
$box = id(new PHUIObjectBoxView())
|
|
|
|
->setHeaderText($title)
|
2014-09-18 17:22:54 +02:00
|
|
|
->addPropertyList($properties)
|
2014-09-18 17:22:18 +02:00
|
|
|
->appendChild($table);
|
|
|
|
|
|
|
|
return $this->buildResponse($title, $box);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function renderDatabase(
|
2014-09-18 17:22:54 +02:00
|
|
|
PhabricatorConfigServerSchema $comp,
|
2014-09-18 17:22:18 +02:00
|
|
|
PhabricatorConfigServerSchema $expect,
|
2014-09-18 17:22:54 +02:00
|
|
|
PhabricatorConfigServerSchema $actual,
|
2014-09-18 17:22:18 +02:00
|
|
|
$database_name) {
|
|
|
|
|
2014-09-18 17:25:34 +02:00
|
|
|
$collation_issue = PhabricatorConfigStorageSchema::ISSUE_COLLATION;
|
|
|
|
|
2014-09-18 17:22:54 +02:00
|
|
|
$database = $comp->getDatabase($database_name);
|
2014-09-18 17:22:18 +02:00
|
|
|
if (!$database) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
|
|
|
|
$rows = array();
|
|
|
|
foreach ($database->getTables() as $table_name => $table) {
|
2014-09-18 17:22:54 +02:00
|
|
|
$status = $table->getStatus();
|
|
|
|
|
2014-09-18 17:22:18 +02:00
|
|
|
$rows[] = array(
|
2014-09-18 17:22:54 +02:00
|
|
|
$this->renderIcon($status),
|
2014-09-18 17:22:18 +02:00
|
|
|
phutil_tag(
|
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => $this->getApplicationURI(
|
|
|
|
'/database/'.$database_name.'/'.$table_name.'/'),
|
|
|
|
),
|
|
|
|
$table_name),
|
2014-09-18 17:25:34 +02:00
|
|
|
$this->renderAttr(
|
|
|
|
$table->getCollation(),
|
|
|
|
$table->hasIssue($collation_issue)),
|
2014-09-18 17:22:18 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
$table = id(new AphrontTableView($rows))
|
|
|
|
->setHeaders(
|
|
|
|
array(
|
2014-09-18 17:22:54 +02:00
|
|
|
null,
|
2014-09-18 17:22:18 +02:00
|
|
|
pht('Table'),
|
|
|
|
pht('Collation'),
|
|
|
|
))
|
|
|
|
->setColumnClasses(
|
|
|
|
array(
|
2014-09-18 17:22:54 +02:00
|
|
|
null,
|
2014-09-18 17:22:18 +02:00
|
|
|
'wide pri',
|
|
|
|
null,
|
|
|
|
));
|
|
|
|
|
|
|
|
$title = pht('Database Status: %s', $database_name);
|
|
|
|
|
2014-09-18 17:22:54 +02:00
|
|
|
$actual_database = $actual->getDatabase($database_name);
|
|
|
|
if ($actual_database) {
|
|
|
|
$actual_charset = $actual_database->getCharacterSet();
|
|
|
|
$actual_collation = $actual_database->getCollation();
|
|
|
|
} else {
|
|
|
|
$actual_charset = null;
|
|
|
|
$actual_collation = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$expect_database = $expect->getDatabase($database_name);
|
|
|
|
if ($expect_database) {
|
|
|
|
$expect_charset = $expect_database->getCharacterSet();
|
|
|
|
$expect_collation = $expect_database->getCollation();
|
|
|
|
} else {
|
|
|
|
$expect_charset = null;
|
|
|
|
$expect_collation = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$properties = $this->buildProperties(
|
|
|
|
array(
|
|
|
|
array(
|
|
|
|
pht('Character Set'),
|
|
|
|
$actual_charset,
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
pht('Expected Character Set'),
|
|
|
|
$expect_charset,
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
pht('Collation'),
|
|
|
|
$actual_collation,
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
pht('Expected Collation'),
|
|
|
|
$expect_collation,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
$database->getIssues());
|
|
|
|
|
2014-09-18 17:22:18 +02:00
|
|
|
$box = id(new PHUIObjectBoxView())
|
|
|
|
->setHeaderText($title)
|
2014-09-18 17:22:54 +02:00
|
|
|
->addPropertyList($properties)
|
2014-09-18 17:22:18 +02:00
|
|
|
->appendChild($table);
|
|
|
|
|
|
|
|
return $this->buildResponse($title, $box);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function renderTable(
|
2014-09-18 17:22:54 +02:00
|
|
|
PhabricatorConfigServerSchema $comp,
|
2014-09-18 17:22:18 +02:00
|
|
|
PhabricatorConfigServerSchema $expect,
|
2014-09-18 17:22:54 +02:00
|
|
|
PhabricatorConfigServerSchema $actual,
|
2014-09-18 17:22:18 +02:00
|
|
|
$database_name,
|
|
|
|
$table_name) {
|
|
|
|
|
2014-09-18 17:25:34 +02:00
|
|
|
$type_issue = PhabricatorConfigStorageSchema::ISSUE_COLUMNTYPE;
|
|
|
|
$charset_issue = PhabricatorConfigStorageSchema::ISSUE_CHARSET;
|
|
|
|
$collation_issue = PhabricatorConfigStorageSchema::ISSUE_COLLATION;
|
2014-09-18 17:32:21 +02:00
|
|
|
$nullable_issue = PhabricatorConfigStorageSchema::ISSUE_NULLABLE;
|
2014-09-18 17:25:34 +02:00
|
|
|
|
2014-09-18 17:22:54 +02:00
|
|
|
$database = $comp->getDatabase($database_name);
|
2014-09-18 17:22:18 +02:00
|
|
|
if (!$database) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
|
|
|
|
$table = $database->getTable($table_name);
|
|
|
|
if (!$table) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
|
2014-09-18 17:25:34 +02:00
|
|
|
$actual_database = $actual->getDatabase($database_name);
|
|
|
|
$actual_table = null;
|
|
|
|
if ($actual_database) {
|
|
|
|
$actual_table = $actual_database->getTable($table_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
$expect_database = $expect->getDatabase($database_name);
|
|
|
|
$expect_table = null;
|
|
|
|
if ($expect_database) {
|
|
|
|
$expect_table = $expect_database->getTable($table_name);
|
|
|
|
}
|
|
|
|
|
2014-09-18 17:22:18 +02:00
|
|
|
$rows = array();
|
|
|
|
foreach ($table->getColumns() as $column_name => $column) {
|
2014-09-18 17:25:34 +02:00
|
|
|
$expect_column = null;
|
|
|
|
if ($expect_table) {
|
|
|
|
$expect_column = $expect_table->getColumn($column_name);
|
|
|
|
}
|
|
|
|
|
2014-09-18 17:22:54 +02:00
|
|
|
$status = $column->getStatus();
|
|
|
|
|
2014-09-18 17:25:34 +02:00
|
|
|
$data_type = null;
|
|
|
|
if ($expect_column) {
|
|
|
|
$data_type = $expect_column->getDataType();
|
|
|
|
}
|
|
|
|
|
2014-09-18 17:22:18 +02:00
|
|
|
$rows[] = array(
|
2014-09-18 17:22:54 +02:00
|
|
|
$this->renderIcon($status),
|
|
|
|
phutil_tag(
|
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => $this->getApplicationURI(
|
|
|
|
'database/'.
|
|
|
|
$database_name.'/'.
|
|
|
|
$table_name.'/'.
|
2014-09-18 17:32:21 +02:00
|
|
|
'col/'.
|
2014-09-18 17:22:54 +02:00
|
|
|
$column_name.'/'),
|
|
|
|
),
|
|
|
|
$column_name),
|
2014-09-18 17:25:34 +02:00
|
|
|
$data_type,
|
|
|
|
$this->renderAttr(
|
|
|
|
$column->getColumnType(),
|
|
|
|
$column->hasIssue($type_issue)),
|
2014-09-18 17:32:21 +02:00
|
|
|
$this->renderAttr(
|
|
|
|
$column->getNullable()
|
|
|
|
? pht('Yes')
|
|
|
|
: pht('No'),
|
|
|
|
$column->hasIssue($nullable_issue)),
|
2014-09-18 17:25:34 +02:00
|
|
|
$this->renderAttr(
|
|
|
|
$column->getCharacterSet(),
|
|
|
|
$column->hasIssue($charset_issue)),
|
|
|
|
$this->renderAttr(
|
|
|
|
$column->getCollation(),
|
|
|
|
$column->hasIssue($collation_issue)),
|
2014-09-18 17:22:18 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2014-09-18 17:22:54 +02:00
|
|
|
$table_view = id(new AphrontTableView($rows))
|
2014-09-18 17:22:18 +02:00
|
|
|
->setHeaders(
|
|
|
|
array(
|
2014-09-18 17:22:54 +02:00
|
|
|
null,
|
2014-09-18 17:32:21 +02:00
|
|
|
pht('Column'),
|
2014-09-18 17:25:34 +02:00
|
|
|
pht('Data Type'),
|
2014-09-18 17:22:18 +02:00
|
|
|
pht('Column Type'),
|
2014-09-18 17:32:21 +02:00
|
|
|
pht('Nullable'),
|
2014-09-18 17:22:18 +02:00
|
|
|
pht('Character Set'),
|
|
|
|
pht('Collation'),
|
|
|
|
))
|
|
|
|
->setColumnClasses(
|
|
|
|
array(
|
2014-09-18 17:22:54 +02:00
|
|
|
null,
|
2014-09-18 17:22:18 +02:00
|
|
|
'wide pri',
|
|
|
|
null,
|
|
|
|
null,
|
2014-09-18 17:25:34 +02:00
|
|
|
null,
|
2014-09-18 17:22:18 +02:00
|
|
|
null
|
|
|
|
));
|
|
|
|
|
2014-09-18 17:32:21 +02:00
|
|
|
$key_rows = array();
|
|
|
|
foreach ($table->getKeys() as $key_name => $key) {
|
|
|
|
$expect_key = null;
|
|
|
|
if ($expect_table) {
|
|
|
|
$expect_key = $expect_table->getKey($key_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
$status = $key->getStatus();
|
|
|
|
|
|
|
|
$size = 0;
|
|
|
|
foreach ($key->getColumnNames() as $column_name) {
|
|
|
|
$column = $table->getColumn($column_name);
|
|
|
|
if (!$column) {
|
|
|
|
$size = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
$size += $column->getKeyByteLength();
|
|
|
|
}
|
|
|
|
|
|
|
|
$size_formatted = null;
|
|
|
|
if ($size) {
|
|
|
|
$size_formatted = $this->renderAttr(
|
|
|
|
$size,
|
|
|
|
($size > self::MAX_INNODB_KEY_LENGTH));
|
|
|
|
}
|
|
|
|
|
|
|
|
$key_rows[] = array(
|
|
|
|
$this->renderIcon($status),
|
|
|
|
phutil_tag(
|
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => $this->getApplicationURI(
|
|
|
|
'database/'.
|
|
|
|
$database_name.'/'.
|
|
|
|
$table_name.'/'.
|
|
|
|
'key/'.
|
|
|
|
$key_name.'/'),
|
|
|
|
),
|
|
|
|
$key_name),
|
|
|
|
implode(', ', $key->getColumnNames()),
|
|
|
|
$size_formatted,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
$keys_view = id(new AphrontTableView($key_rows))
|
|
|
|
->setHeaders(
|
|
|
|
array(
|
|
|
|
null,
|
|
|
|
pht('Key'),
|
|
|
|
pht('Columns'),
|
|
|
|
pht('Size'),
|
|
|
|
))
|
|
|
|
->setColumnClasses(
|
|
|
|
array(
|
|
|
|
null,
|
|
|
|
'wide pri',
|
|
|
|
null,
|
|
|
|
null,
|
|
|
|
));
|
|
|
|
|
2014-09-18 17:22:18 +02:00
|
|
|
$title = pht('Database Status: %s.%s', $database_name, $table_name);
|
|
|
|
|
2014-09-18 17:25:34 +02:00
|
|
|
if ($actual_table) {
|
|
|
|
$actual_collation = $actual_table->getCollation();
|
|
|
|
} else {
|
|
|
|
$actual_collation = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($expect_table) {
|
|
|
|
$expect_collation = $expect_table->getCollation();
|
|
|
|
} else {
|
|
|
|
$expect_collation = null;
|
|
|
|
}
|
|
|
|
|
2014-09-18 17:22:54 +02:00
|
|
|
$properties = $this->buildProperties(
|
|
|
|
array(
|
2014-09-18 17:25:34 +02:00
|
|
|
array(
|
|
|
|
pht('Collation'),
|
|
|
|
$actual_collation,
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
pht('Expected Collation'),
|
|
|
|
$expect_collation,
|
|
|
|
),
|
2014-09-18 17:22:54 +02:00
|
|
|
),
|
|
|
|
$table->getIssues());
|
|
|
|
|
2014-09-18 17:22:18 +02:00
|
|
|
$box = id(new PHUIObjectBoxView())
|
|
|
|
->setHeaderText($title)
|
2014-09-18 17:22:54 +02:00
|
|
|
->addPropertyList($properties)
|
2014-09-18 17:32:21 +02:00
|
|
|
->appendChild($table_view)
|
|
|
|
->appendChild($keys_view);
|
2014-09-18 17:22:54 +02:00
|
|
|
|
|
|
|
return $this->buildResponse($title, $box);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function renderColumn(
|
|
|
|
PhabricatorConfigServerSchema $comp,
|
|
|
|
PhabricatorConfigServerSchema $expect,
|
|
|
|
PhabricatorConfigServerSchema $actual,
|
|
|
|
$database_name,
|
|
|
|
$table_name,
|
|
|
|
$column_name) {
|
|
|
|
|
|
|
|
$database = $comp->getDatabase($database_name);
|
|
|
|
if (!$database) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
|
|
|
|
$table = $database->getTable($table_name);
|
|
|
|
if (!$table) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
|
|
|
|
$column = $table->getColumn($column_name);
|
2014-09-18 17:32:21 +02:00
|
|
|
if (!$column) {
|
2014-09-18 17:22:54 +02:00
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
|
2014-09-18 17:25:34 +02:00
|
|
|
$actual_database = $actual->getDatabase($database_name);
|
|
|
|
$actual_table = null;
|
|
|
|
$actual_column = null;
|
|
|
|
if ($actual_database) {
|
|
|
|
$actual_table = $actual_database->getTable($table_name);
|
|
|
|
if ($actual_table) {
|
|
|
|
$actual_column = $actual_table->getColumn($column_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$expect_database = $expect->getDatabase($database_name);
|
|
|
|
$expect_table = null;
|
|
|
|
$expect_column = null;
|
|
|
|
if ($expect_database) {
|
|
|
|
$expect_table = $expect_database->getTable($table_name);
|
|
|
|
if ($expect_table) {
|
|
|
|
$expect_column = $expect_table->getColumn($column_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($actual_column) {
|
|
|
|
$actual_coltype = $actual_column->getColumnType();
|
|
|
|
$actual_charset = $actual_column->getCharacterSet();
|
|
|
|
$actual_collation = $actual_column->getCollation();
|
2014-09-18 17:32:44 +02:00
|
|
|
$actual_nullable = $actual_column->getNullable();
|
2014-09-18 17:25:34 +02:00
|
|
|
} else {
|
|
|
|
$actual_coltype = null;
|
|
|
|
$actual_charset = null;
|
|
|
|
$actual_collation = null;
|
2014-09-18 17:32:44 +02:00
|
|
|
$actual_nullable = null;
|
2014-09-18 17:25:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($expect_column) {
|
|
|
|
$data_type = $expect_column->getDataType();
|
|
|
|
$expect_coltype = $expect_column->getColumnType();
|
|
|
|
$expect_charset = $expect_column->getCharacterSet();
|
|
|
|
$expect_collation = $expect_column->getCollation();
|
2014-09-18 17:32:44 +02:00
|
|
|
$expect_nullable = $expect_column->getNullable();
|
2014-09-18 17:25:34 +02:00
|
|
|
} else {
|
|
|
|
$data_type = null;
|
|
|
|
$expect_coltype = null;
|
|
|
|
$expect_charset = null;
|
|
|
|
$expect_collation = null;
|
2014-09-18 17:32:44 +02:00
|
|
|
$expect_nullable = null;
|
2014-09-18 17:25:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-09-18 17:22:54 +02:00
|
|
|
$title = pht(
|
|
|
|
'Database Status: %s.%s.%s',
|
|
|
|
$database_name,
|
|
|
|
$table_name,
|
|
|
|
$column_name);
|
|
|
|
|
|
|
|
$properties = $this->buildProperties(
|
|
|
|
array(
|
2014-09-18 17:25:34 +02:00
|
|
|
array(
|
|
|
|
pht('Data Type'),
|
|
|
|
$data_type,
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
pht('Column Type'),
|
|
|
|
$actual_coltype,
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
pht('Expected Column Type'),
|
|
|
|
$expect_coltype,
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
pht('Character Set'),
|
|
|
|
$actual_charset,
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
pht('Expected Character Set'),
|
|
|
|
$expect_charset,
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
pht('Collation'),
|
|
|
|
$actual_collation,
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
pht('Expected Collation'),
|
|
|
|
$expect_collation,
|
|
|
|
),
|
2014-09-18 17:32:44 +02:00
|
|
|
array(
|
|
|
|
pht('Nullable'),
|
|
|
|
$this->getNullableString($actual_nullable),
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
pht('Expected Nullable'),
|
|
|
|
$this->getNullableString($expect_nullable),
|
|
|
|
),
|
2014-09-18 17:22:54 +02:00
|
|
|
),
|
|
|
|
$column->getIssues());
|
|
|
|
|
|
|
|
$box = id(new PHUIObjectBoxView())
|
|
|
|
->setHeaderText($title)
|
|
|
|
->addPropertyList($properties);
|
2014-09-18 17:22:18 +02:00
|
|
|
|
|
|
|
return $this->buildResponse($title, $box);
|
|
|
|
}
|
2014-09-18 17:32:21 +02:00
|
|
|
|
|
|
|
private function renderKey(
|
|
|
|
PhabricatorConfigServerSchema $comp,
|
|
|
|
PhabricatorConfigServerSchema $expect,
|
|
|
|
PhabricatorConfigServerSchema $actual,
|
|
|
|
$database_name,
|
|
|
|
$table_name,
|
|
|
|
$key_name) {
|
|
|
|
|
|
|
|
$database = $comp->getDatabase($database_name);
|
|
|
|
if (!$database) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
|
|
|
|
$table = $database->getTable($table_name);
|
|
|
|
if (!$table) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
|
|
|
|
$key = $table->getKey($key_name);
|
|
|
|
if (!$key) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
|
|
|
|
$actual_database = $actual->getDatabase($database_name);
|
|
|
|
$actual_table = null;
|
|
|
|
$actual_key = null;
|
|
|
|
if ($actual_database) {
|
|
|
|
$actual_table = $actual_database->getTable($table_name);
|
|
|
|
if ($actual_table) {
|
|
|
|
$actual_key = $actual_table->getKey($key_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$expect_database = $expect->getDatabase($database_name);
|
|
|
|
$expect_table = null;
|
|
|
|
$expect_key = null;
|
|
|
|
if ($expect_database) {
|
|
|
|
$expect_table = $expect_database->getTable($table_name);
|
|
|
|
if ($expect_table) {
|
|
|
|
$expect_key = $expect_table->getKey($key_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($actual_key) {
|
|
|
|
$actual_columns = $actual_key->getColumnNames();
|
|
|
|
} else {
|
|
|
|
$actual_columns = array();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($expect_key) {
|
|
|
|
$expect_columns = $expect_key->getColumnNames();
|
|
|
|
} else {
|
|
|
|
$expect_columns = array();
|
|
|
|
}
|
|
|
|
|
|
|
|
$title = pht(
|
|
|
|
'Database Status: %s.%s (%s)',
|
|
|
|
$database_name,
|
|
|
|
$table_name,
|
|
|
|
$key_name);
|
|
|
|
|
|
|
|
$properties = $this->buildProperties(
|
|
|
|
array(
|
|
|
|
array(
|
|
|
|
pht('Columns'),
|
|
|
|
implode(', ', $actual_columns),
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
pht('Expected Columns'),
|
|
|
|
implode(', ', $expect_columns),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
$key->getIssues());
|
|
|
|
|
|
|
|
$box = id(new PHUIObjectBoxView())
|
|
|
|
->setHeaderText($title)
|
|
|
|
->addPropertyList($properties);
|
|
|
|
|
|
|
|
return $this->buildResponse($title, $box);
|
|
|
|
}
|
|
|
|
|
2014-09-18 17:22:54 +02:00
|
|
|
private function renderIcon($status) {
|
|
|
|
switch ($status) {
|
|
|
|
case PhabricatorConfigStorageSchema::STATUS_OKAY:
|
|
|
|
$icon = 'fa-check-circle green';
|
|
|
|
break;
|
2014-09-18 20:15:49 +02:00
|
|
|
case PhabricatorConfigStorageSchema::STATUS_NOTE:
|
|
|
|
$icon = 'fa-info-circle blue';
|
|
|
|
break;
|
2014-09-18 17:22:54 +02:00
|
|
|
case PhabricatorConfigStorageSchema::STATUS_WARN:
|
|
|
|
$icon = 'fa-exclamation-circle yellow';
|
|
|
|
break;
|
|
|
|
case PhabricatorConfigStorageSchema::STATUS_FAIL:
|
|
|
|
default:
|
|
|
|
$icon = 'fa-times-circle red';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return id(new PHUIIconView())
|
|
|
|
->setIconFont($icon);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function renderAttr($attr, $issue) {
|
|
|
|
if ($issue) {
|
|
|
|
return phutil_tag(
|
|
|
|
'span',
|
|
|
|
array(
|
|
|
|
'style' => 'color: #aa0000;',
|
|
|
|
),
|
|
|
|
$attr);
|
|
|
|
} else {
|
|
|
|
return $attr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2014-09-18 20:15:49 +02:00
|
|
|
case PhabricatorConfigStorageSchema::STATUS_NOTE:
|
|
|
|
$icon = PHUIStatusItemView::ICON_INFO;
|
|
|
|
$color = 'blue';
|
|
|
|
break;
|
2014-09-18 17:22:54 +02:00
|
|
|
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 $view;
|
|
|
|
}
|
2014-09-18 17:22:18 +02:00
|
|
|
|
2014-09-18 17:32:44 +02:00
|
|
|
private function getNullableString($value) {
|
|
|
|
if ($value === null) {
|
|
|
|
return '';
|
|
|
|
} else if ($value === true) {
|
|
|
|
return pht('Yes');
|
|
|
|
} else {
|
|
|
|
return pht('No');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-18 17:22:18 +02:00
|
|
|
}
|