Path: blob/master/src/applications/config/controller/services/PhabricatorConfigDatabaseStatusController.php
12262 views
<?php12final class PhabricatorConfigDatabaseStatusController3extends PhabricatorConfigDatabaseController {45private $database;6private $table;7private $column;8private $key;9private $ref;1011public function handleRequest(AphrontRequest $request) {12$viewer = $request->getViewer();13$this->database = $request->getURIData('database');14$this->table = $request->getURIData('table');15$this->column = $request->getURIData('column');16$this->key = $request->getURIData('key');17$this->ref = $request->getURIData('ref');1819$query = new PhabricatorConfigSchemaQuery();2021$actual = $query->loadActualSchemata();22$expect = $query->loadExpectedSchemata();23$comp = $query->buildComparisonSchemata($expect, $actual);2425if ($this->ref !== null) {26$server_actual = idx($actual, $this->ref);27if (!$server_actual) {28return new Aphront404Response();29}3031$server_comparison = $comp[$this->ref];32$server_expect = $expect[$this->ref];3334if ($this->column) {35return $this->renderColumn(36$server_comparison,37$server_expect,38$server_actual,39$this->database,40$this->table,41$this->column);42} else if ($this->key) {43return $this->renderKey(44$server_comparison,45$server_expect,46$server_actual,47$this->database,48$this->table,49$this->key);50} else if ($this->table) {51return $this->renderTable(52$server_comparison,53$server_expect,54$server_actual,55$this->database,56$this->table);57} else if ($this->database) {58return $this->renderDatabase(59$server_comparison,60$server_expect,61$server_actual,62$this->database);63}64}6566return $this->renderServers(67$comp,68$expect,69$actual);70}7172private function buildResponse($title, $body) {73$nav = $this->newNavigation('schemata');7475if (!$title) {76$title = pht('Database Status');77}7879$ref = $this->ref;80$database = $this->database;81$table = $this->table;82$column = $this->column;83$key = $this->key;8485$links = array();86$links[] = array(87pht('Database Status'),88'database/',89);9091if ($database) {92$links[] = array(93$database,94"database/{$ref}/{$database}/",95);96}9798if ($table) {99$links[] = array(100$table,101"database/{$ref}/{$database}/{$table}/",102);103}104105if ($column) {106$links[] = array(107$column,108"database/{$ref}/{$database}/{$table}/col/{$column}/",109);110}111112if ($key) {113$links[] = array(114$key,115"database/{$ref}/{$database}/{$table}/key/{$key}/",116);117}118119$crumbs = $this->newCrumbs();120121$last_key = last_key($links);122foreach ($links as $link_key => $link) {123list($name, $href) = $link;124if ($link_key == $last_key) {125$crumbs->addTextCrumb($name);126} else {127$crumbs->addTextCrumb($name, $this->getApplicationURI($href));128}129}130131$doc_link = PhabricatorEnv::getDoclink('Managing Storage Adjustments');132$button = id(new PHUIButtonView())133->setTag('a')134->setIcon('fa-book')135->setHref($doc_link)136->setText(pht('Documentation'));137138$header = $this->buildHeaderView($title, $button);139140$content = id(new PHUITwoColumnView())141->setHeader($header)142->setFooter($body);143144return $this->newPage()145->setTitle($title)146->setCrumbs($crumbs)147->setNavigation($nav)148->appendChild($content);149}150151152private function renderServers(153array $comp_servers,154array $expect_servers,155array $actual_servers) {156157$charset_issue = PhabricatorConfigStorageSchema::ISSUE_CHARSET;158$collation_issue = PhabricatorConfigStorageSchema::ISSUE_COLLATION;159160$rows = array();161foreach ($comp_servers as $ref_key => $comp) {162$actual = $actual_servers[$ref_key];163$expect = $expect_servers[$ref_key];164foreach ($comp->getDatabases() as $database_name => $database) {165$actual_database = $actual->getDatabase($database_name);166if ($actual_database) {167$charset = $actual_database->getCharacterSet();168$collation = $actual_database->getCollation();169} else {170$charset = null;171$collation = null;172}173174$status = $database->getStatus();175$issues = $database->getIssues();176177$uri = $this->getURI(178array(179'ref' => $ref_key,180'database' => $database_name,181));182183$rows[] = array(184$this->renderIcon($status),185$ref_key,186phutil_tag(187'a',188array(189'href' => $uri,190),191$database_name),192$this->renderAttr($charset, $database->hasIssue($charset_issue)),193$this->renderAttr($collation, $database->hasIssue($collation_issue)),194);195}196}197198$table = id(new AphrontTableView($rows))199->setHeaders(200array(201null,202pht('Server'),203pht('Database'),204pht('Charset'),205pht('Collation'),206))207->setColumnClasses(208array(209null,210null,211'wide pri',212null,213null,214));215216$title = pht('Database Status');217$properties = $this->buildProperties(218array(219),220$comp->getIssues());221$properties = $this->buildConfigBoxView(pht('Properties'), $properties);222$table = $this->buildConfigBoxView(pht('Database'), $table);223224return $this->buildResponse($title, array($properties, $table));225}226227private function renderDatabase(228PhabricatorConfigServerSchema $comp,229PhabricatorConfigServerSchema $expect,230PhabricatorConfigServerSchema $actual,231$database_name) {232233$collation_issue = PhabricatorConfigStorageSchema::ISSUE_COLLATION;234235$database = $comp->getDatabase($database_name);236if (!$database) {237return new Aphront404Response();238}239240$rows = array();241foreach ($database->getTables() as $table_name => $table) {242$status = $table->getStatus();243244$uri = $this->getURI(245array(246'table' => $table_name,247));248249$rows[] = array(250$this->renderIcon($status),251phutil_tag(252'a',253array(254'href' => $uri,255),256$table_name),257$this->renderAttr(258$table->getCollation(),259$table->hasIssue($collation_issue)),260$table->getPersistenceTypeDisplayName(),261);262}263264$table = id(new AphrontTableView($rows))265->setHeaders(266array(267null,268pht('Table'),269pht('Collation'),270pht('Persistence'),271))272->setColumnClasses(273array(274null,275'wide pri',276null,277null,278));279280$title = $database_name;281282$actual_database = $actual->getDatabase($database_name);283if ($actual_database) {284$actual_charset = $actual_database->getCharacterSet();285$actual_collation = $actual_database->getCollation();286} else {287$actual_charset = null;288$actual_collation = null;289}290291$expect_database = $expect->getDatabase($database_name);292if ($expect_database) {293$expect_charset = $expect_database->getCharacterSet();294$expect_collation = $expect_database->getCollation();295} else {296$expect_charset = null;297$expect_collation = null;298}299300$properties = $this->buildProperties(301array(302array(303pht('Server'),304$this->ref,305),306array(307pht('Character Set'),308$actual_charset,309),310array(311pht('Expected Character Set'),312$expect_charset,313),314array(315pht('Collation'),316$actual_collation,317),318array(319pht('Expected Collation'),320$expect_collation,321),322),323$database->getIssues());324325$properties = $this->buildConfigBoxView(pht('Properties'), $properties);326$table = $this->buildConfigBoxView(pht('Database'), $table);327328return $this->buildResponse($title, array($properties, $table));329}330331private function renderTable(332PhabricatorConfigServerSchema $comp,333PhabricatorConfigServerSchema $expect,334PhabricatorConfigServerSchema $actual,335$database_name,336$table_name) {337338$type_issue = PhabricatorConfigStorageSchema::ISSUE_COLUMNTYPE;339$charset_issue = PhabricatorConfigStorageSchema::ISSUE_CHARSET;340$collation_issue = PhabricatorConfigStorageSchema::ISSUE_COLLATION;341$nullable_issue = PhabricatorConfigStorageSchema::ISSUE_NULLABLE;342$unique_issue = PhabricatorConfigStorageSchema::ISSUE_UNIQUE;343$columns_issue = PhabricatorConfigStorageSchema::ISSUE_KEYCOLUMNS;344$longkey_issue = PhabricatorConfigStorageSchema::ISSUE_LONGKEY;345$auto_issue = PhabricatorConfigStorageSchema::ISSUE_AUTOINCREMENT;346347$database = $comp->getDatabase($database_name);348if (!$database) {349return new Aphront404Response();350}351352$table = $database->getTable($table_name);353if (!$table) {354return new Aphront404Response();355}356357$actual_database = $actual->getDatabase($database_name);358$actual_table = null;359if ($actual_database) {360$actual_table = $actual_database->getTable($table_name);361}362363$expect_database = $expect->getDatabase($database_name);364$expect_table = null;365if ($expect_database) {366$expect_table = $expect_database->getTable($table_name);367}368369$rows = array();370foreach ($table->getColumns() as $column_name => $column) {371$expect_column = null;372if ($expect_table) {373$expect_column = $expect_table->getColumn($column_name);374}375376$status = $column->getStatus();377378$data_type = null;379if ($expect_column) {380$data_type = $expect_column->getDataType();381}382383$uri = $this->getURI(384array(385'column' => $column_name,386));387388$rows[] = array(389$this->renderIcon($status),390phutil_tag(391'a',392array(393'href' => $uri,394),395$column_name),396$data_type,397$this->renderAttr(398$column->getColumnType(),399$column->hasIssue($type_issue)),400$this->renderAttr(401$this->renderBoolean($column->getNullable()),402$column->hasIssue($nullable_issue)),403$this->renderAttr(404$this->renderBoolean($column->getAutoIncrement()),405$column->hasIssue($auto_issue)),406$this->renderAttr(407$column->getCharacterSet(),408$column->hasIssue($charset_issue)),409$this->renderAttr(410$column->getCollation(),411$column->hasIssue($collation_issue)),412);413}414415$table_view = id(new AphrontTableView($rows))416->setHeaders(417array(418null,419pht('Column'),420pht('Data Type'),421pht('Column Type'),422pht('Nullable'),423pht('Autoincrement'),424pht('Character Set'),425pht('Collation'),426))427->setColumnClasses(428array(429null,430'wide pri',431null,432null,433null,434null,435null,436));437438$key_rows = array();439foreach ($table->getKeys() as $key_name => $key) {440$expect_key = null;441if ($expect_table) {442$expect_key = $expect_table->getKey($key_name);443}444445$status = $key->getStatus();446447$size = 0;448foreach ($key->getColumnNames() as $column_spec) {449list($column_name, $prefix) = $key->getKeyColumnAndPrefix($column_spec);450$column = $table->getColumn($column_name);451if (!$column) {452$size = 0;453break;454}455$size += $column->getKeyByteLength($prefix);456}457458$size_formatted = null;459if ($size) {460$size_formatted = $this->renderAttr(461$size,462$key->hasIssue($longkey_issue));463}464465$uri = $this->getURI(466array(467'key' => $key_name,468));469470$key_rows[] = array(471$this->renderIcon($status),472phutil_tag(473'a',474array(475'href' => $uri,476),477$key_name),478$this->renderAttr(479implode(', ', $key->getColumnNames()),480$key->hasIssue($columns_issue)),481$this->renderAttr(482$this->renderBoolean($key->getUnique()),483$key->hasIssue($unique_issue)),484$size_formatted,485);486}487488$keys_view = id(new AphrontTableView($key_rows))489->setHeaders(490array(491null,492pht('Key'),493pht('Columns'),494pht('Unique'),495pht('Size'),496))497->setColumnClasses(498array(499null,500'wide pri',501null,502null,503null,504));505506$title = pht('%s.%s', $database_name, $table_name);507508if ($actual_table) {509$actual_collation = $actual_table->getCollation();510} else {511$actual_collation = null;512}513514if ($expect_table) {515$expect_collation = $expect_table->getCollation();516} else {517$expect_collation = null;518}519520$properties = $this->buildProperties(521array(522array(523pht('Server'),524$this->ref,525),526array(527pht('Collation'),528$actual_collation,529),530array(531pht('Expected Collation'),532$expect_collation,533),534),535$table->getIssues());536537$box_header = pht('%s.%s', $database_name, $table_name);538539$properties = $this->buildConfigBoxView(pht('Properties'), $properties);540$table = $this->buildConfigBoxView(pht('Database'), $table_view);541$keys = $this->buildConfigBoxView(pht('Keys'), $keys_view);542543return $this->buildResponse(544$title, array($properties, $table, $keys));545}546547private function renderColumn(548PhabricatorConfigServerSchema $comp,549PhabricatorConfigServerSchema $expect,550PhabricatorConfigServerSchema $actual,551$database_name,552$table_name,553$column_name) {554555$database = $comp->getDatabase($database_name);556if (!$database) {557return new Aphront404Response();558}559560$table = $database->getTable($table_name);561if (!$table) {562return new Aphront404Response();563}564565$column = $table->getColumn($column_name);566if (!$column) {567return new Aphront404Response();568}569570$actual_database = $actual->getDatabase($database_name);571$actual_table = null;572$actual_column = null;573if ($actual_database) {574$actual_table = $actual_database->getTable($table_name);575if ($actual_table) {576$actual_column = $actual_table->getColumn($column_name);577}578}579580$expect_database = $expect->getDatabase($database_name);581$expect_table = null;582$expect_column = null;583if ($expect_database) {584$expect_table = $expect_database->getTable($table_name);585if ($expect_table) {586$expect_column = $expect_table->getColumn($column_name);587}588}589590if ($actual_column) {591$actual_coltype = $actual_column->getColumnType();592$actual_charset = $actual_column->getCharacterSet();593$actual_collation = $actual_column->getCollation();594$actual_nullable = $actual_column->getNullable();595$actual_auto = $actual_column->getAutoIncrement();596} else {597$actual_coltype = null;598$actual_charset = null;599$actual_collation = null;600$actual_nullable = null;601$actual_auto = null;602}603604if ($expect_column) {605$data_type = $expect_column->getDataType();606$expect_coltype = $expect_column->getColumnType();607$expect_charset = $expect_column->getCharacterSet();608$expect_collation = $expect_column->getCollation();609$expect_nullable = $expect_column->getNullable();610$expect_auto = $expect_column->getAutoIncrement();611} else {612$data_type = null;613$expect_coltype = null;614$expect_charset = null;615$expect_collation = null;616$expect_nullable = null;617$expect_auto = null;618}619620621$title = pht(622'%s.%s.%s',623$database_name,624$table_name,625$column_name);626627$properties = $this->buildProperties(628array(629array(630pht('Server'),631$this->ref,632),633array(634pht('Data Type'),635$data_type,636),637array(638pht('Column Type'),639$actual_coltype,640),641array(642pht('Expected Column Type'),643$expect_coltype,644),645array(646pht('Character Set'),647$actual_charset,648),649array(650pht('Expected Character Set'),651$expect_charset,652),653array(654pht('Collation'),655$actual_collation,656),657array(658pht('Expected Collation'),659$expect_collation,660),661array(662pht('Nullable'),663$this->renderBoolean($actual_nullable),664),665array(666pht('Expected Nullable'),667$this->renderBoolean($expect_nullable),668),669array(670pht('Autoincrement'),671$this->renderBoolean($actual_auto),672),673array(674pht('Expected Autoincrement'),675$this->renderBoolean($expect_auto),676),677),678$column->getIssues());679680$properties = $this->buildConfigBoxView(pht('Properties'), $properties);681682return $this->buildResponse($title, $properties);683}684685private function renderKey(686PhabricatorConfigServerSchema $comp,687PhabricatorConfigServerSchema $expect,688PhabricatorConfigServerSchema $actual,689$database_name,690$table_name,691$key_name) {692693$database = $comp->getDatabase($database_name);694if (!$database) {695return new Aphront404Response();696}697698$table = $database->getTable($table_name);699if (!$table) {700return new Aphront404Response();701}702703$key = $table->getKey($key_name);704if (!$key) {705return new Aphront404Response();706}707708$actual_database = $actual->getDatabase($database_name);709$actual_table = null;710$actual_key = null;711if ($actual_database) {712$actual_table = $actual_database->getTable($table_name);713if ($actual_table) {714$actual_key = $actual_table->getKey($key_name);715}716}717718$expect_database = $expect->getDatabase($database_name);719$expect_table = null;720$expect_key = null;721if ($expect_database) {722$expect_table = $expect_database->getTable($table_name);723if ($expect_table) {724$expect_key = $expect_table->getKey($key_name);725}726}727728if ($actual_key) {729$actual_columns = $actual_key->getColumnNames();730$actual_unique = $actual_key->getUnique();731} else {732$actual_columns = array();733$actual_unique = null;734}735736if ($expect_key) {737$expect_columns = $expect_key->getColumnNames();738$expect_unique = $expect_key->getUnique();739} else {740$expect_columns = array();741$expect_unique = null;742}743744$title = pht(745'%s.%s (%s)',746$database_name,747$table_name,748$key_name);749750$properties = $this->buildProperties(751array(752array(753pht('Server'),754$this->ref,755),756array(757pht('Unique'),758$this->renderBoolean($actual_unique),759),760array(761pht('Expected Unique'),762$this->renderBoolean($expect_unique),763),764array(765pht('Columns'),766implode(', ', $actual_columns),767),768array(769pht('Expected Columns'),770implode(', ', $expect_columns),771),772),773$key->getIssues());774775$properties = $this->buildConfigBoxView(pht('Properties'), $properties);776777return $this->buildResponse($title, $properties);778}779780private function buildProperties(array $properties, array $issues) {781$view = id(new PHUIPropertyListView())782->setUser($this->getRequest()->getUser());783784foreach ($properties as $property) {785list($key, $value) = $property;786$view->addProperty($key, $value);787}788789$status_view = new PHUIStatusListView();790if (!$issues) {791$status_view->addItem(792id(new PHUIStatusItemView())793->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')794->setTarget(pht('No Schema Issues')));795} else {796foreach ($issues as $issue) {797$note = PhabricatorConfigStorageSchema::getIssueDescription($issue);798799$status = PhabricatorConfigStorageSchema::getIssueStatus($issue);800switch ($status) {801case PhabricatorConfigStorageSchema::STATUS_WARN:802$icon = PHUIStatusItemView::ICON_WARNING;803$color = 'yellow';804break;805case PhabricatorConfigStorageSchema::STATUS_FAIL:806default:807$icon = PHUIStatusItemView::ICON_REJECT;808$color = 'red';809break;810}811812$item = id(new PHUIStatusItemView())813->setTarget(PhabricatorConfigStorageSchema::getIssueName($issue))814->setIcon($icon, $color)815->setNote($note);816817$status_view->addItem($item);818}819}820$view->addProperty(pht('Schema Status'), $status_view);821822return phutil_tag_div('config-page-property', $view);823}824825private function getURI(array $properties) {826$defaults = array(827'ref' => $this->ref,828'database' => $this->database,829'table' => $this->table,830'column' => $this->column,831'key' => $this->key,832);833834$properties = $properties + $defaults;835$properties = array_select_keys($properties, array_keys($defaults));836837$parts = array();838foreach ($properties as $key => $property) {839if ($property === null || !strlen($property)) {840continue;841}842843if ($key == 'column') {844$parts[] = 'col';845} else if ($key == 'key') {846$parts[] = 'key';847}848849$parts[] = $property;850}851852if ($parts) {853$parts = implode('/', $parts).'/';854} else {855$parts = null;856}857858return $this->getApplicationURI('/database/'.$parts);859}860861}862863864