Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13981182
D10499.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
18 KB
Referenced Files
None
Subscribers
None
D10499.id.diff
View Options
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1349,6 +1349,7 @@
'PhabricatorConfigIssueViewController' => 'applications/config/controller/PhabricatorConfigIssueViewController.php',
'PhabricatorConfigJSON' => 'applications/config/json/PhabricatorConfigJSON.php',
'PhabricatorConfigJSONOptionType' => 'applications/config/custom/PhabricatorConfigJSONOptionType.php',
+ 'PhabricatorConfigKeySchema' => 'applications/config/schema/PhabricatorConfigKeySchema.php',
'PhabricatorConfigListController' => 'applications/config/controller/PhabricatorConfigListController.php',
'PhabricatorConfigLocalSource' => 'infrastructure/env/PhabricatorConfigLocalSource.php',
'PhabricatorConfigManagementDeleteWorkflow' => 'applications/config/management/PhabricatorConfigManagementDeleteWorkflow.php',
@@ -4242,6 +4243,7 @@
'PhabricatorConfigIssueListController' => 'PhabricatorConfigController',
'PhabricatorConfigIssueViewController' => 'PhabricatorConfigController',
'PhabricatorConfigJSONOptionType' => 'PhabricatorConfigOptionType',
+ 'PhabricatorConfigKeySchema' => 'PhabricatorConfigStorageSchema',
'PhabricatorConfigListController' => 'PhabricatorConfigController',
'PhabricatorConfigLocalSource' => 'PhabricatorConfigProxySource',
'PhabricatorConfigManagementDeleteWorkflow' => 'PhabricatorConfigManagementWorkflow',
diff --git a/src/applications/config/application/PhabricatorConfigApplication.php b/src/applications/config/application/PhabricatorConfigApplication.php
--- a/src/applications/config/application/PhabricatorConfigApplication.php
+++ b/src/applications/config/application/PhabricatorConfigApplication.php
@@ -45,7 +45,7 @@
'database/'.
'(?:(?P<database>[^/]+)/'.
'(?:(?P<table>[^/]+)/'.
- '(?:(?P<column>[^/]+)/)?)?)?'
+ '(?:(?:col/(?P<column>[^/]+)|key/(?P<key>[^/]+))/)?)?)?'
=> 'PhabricatorConfigDatabaseController',
'(?P<verb>ignore|unignore)/(?P<key>[^/]+)/'
=> 'PhabricatorConfigIgnoreController',
diff --git a/src/applications/config/controller/PhabricatorConfigDatabaseController.php b/src/applications/config/controller/PhabricatorConfigDatabaseController.php
--- a/src/applications/config/controller/PhabricatorConfigDatabaseController.php
+++ b/src/applications/config/controller/PhabricatorConfigDatabaseController.php
@@ -3,14 +3,18 @@
final class PhabricatorConfigDatabaseController
extends PhabricatorConfigController {
+ const MAX_INNODB_KEY_LENGTH = 767;
+
private $database;
private $table;
private $column;
+ private $key;
public function willProcessRequest(array $data) {
$this->database = idx($data, 'database');
$this->table = idx($data, 'table');
$this->column = idx($data, 'column');
+ $this->key = idx($data, 'key');
}
public function processRequest() {
@@ -43,6 +47,14 @@
$this->database,
$this->table,
$this->column);
+ } else if ($this->key) {
+ return $this->renderKey(
+ $comp,
+ $expect,
+ $actual,
+ $this->database,
+ $this->table,
+ $this->key);
} else if ($this->table) {
return $this->renderTable(
$comp,
@@ -77,12 +89,16 @@
$crumbs->addTextCrumb(
$this->database,
$this->getApplicationURI('database/'.$this->database.'/'));
- if ($this->column) {
+ if ($this->column || $this->key) {
$crumbs->addTextCrumb(
$this->table,
$this->getApplicationURI(
'database/'.$this->database.'/'.$this->table.'/'));
- $crumbs->addTextCrumb($this->column);
+ if ($this->column) {
+ $crumbs->addTextCrumb($this->column);
+ } else {
+ $crumbs->addTextCrumb($this->key);
+ }
} else {
$crumbs->addTextCrumb($this->table);
}
@@ -276,6 +292,7 @@
$type_issue = PhabricatorConfigStorageSchema::ISSUE_COLUMNTYPE;
$charset_issue = PhabricatorConfigStorageSchema::ISSUE_CHARSET;
$collation_issue = PhabricatorConfigStorageSchema::ISSUE_COLLATION;
+ $nullable_issue = PhabricatorConfigStorageSchema::ISSUE_NULLABLE;
$database = $comp->getDatabase($database_name);
if (!$database) {
@@ -322,6 +339,7 @@
'database/'.
$database_name.'/'.
$table_name.'/'.
+ 'col/'.
$column_name.'/'),
),
$column_name),
@@ -330,6 +348,11 @@
$column->getColumnType(),
$column->hasIssue($type_issue)),
$this->renderAttr(
+ $column->getNullable()
+ ? pht('Yes')
+ : pht('No'),
+ $column->hasIssue($nullable_issue)),
+ $this->renderAttr(
$column->getCharacterSet(),
$column->hasIssue($charset_issue)),
$this->renderAttr(
@@ -342,9 +365,10 @@
->setHeaders(
array(
null,
- pht('Table'),
+ pht('Column'),
pht('Data Type'),
pht('Column Type'),
+ pht('Nullable'),
pht('Character Set'),
pht('Collation'),
))
@@ -358,6 +382,66 @@
null
));
+ $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,
+ ));
+
$title = pht('Database Status: %s.%s', $database_name, $table_name);
if ($actual_table) {
@@ -388,7 +472,8 @@
$box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->addPropertyList($properties)
- ->appendChild($table_view);
+ ->appendChild($table_view)
+ ->appendChild($keys_view);
return $this->buildResponse($title, $box);
}
@@ -412,7 +497,7 @@
}
$column = $table->getColumn($column_name);
- if (!$table) {
+ if (!$column) {
return new Aphront404Response();
}
@@ -504,6 +589,88 @@
return $this->buildResponse($title, $box);
}
+
+ 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);
+ }
+
private function renderIcon($status) {
switch ($status) {
case PhabricatorConfigStorageSchema::STATUS_OKAY:
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
@@ -7,6 +7,16 @@
private $collation;
private $columnType;
private $dataType;
+ private $nullable;
+
+ public function setNullable($nullable) {
+ $this->nullable = $nullable;
+ return $this;
+ }
+
+ public function getNullable() {
+ return $this->nullable;
+ }
public function setColumnType($column_type) {
$this->columnType = $column_type;
@@ -48,6 +58,20 @@
return $this->characterSet;
}
+ public function getKeyByteLength() {
+ $type = $this->getColumnType();
+
+ $matches = null;
+ if (preg_match('/^varchar\((\d+)\)$/', $type, $matches)) {
+ // For utf8mb4, each character requires 4 bytes.
+ return ((int)$matches[1]) * 4;
+ }
+
+ // TODO: Build this out to catch overlong indexes.
+
+ return 0;
+ }
+
public function compareToSimilarSchema(
PhabricatorConfigStorageSchema $expect) {
diff --git a/src/applications/config/schema/PhabricatorConfigKeySchema.php b/src/applications/config/schema/PhabricatorConfigKeySchema.php
new file mode 100644
--- /dev/null
+++ b/src/applications/config/schema/PhabricatorConfigKeySchema.php
@@ -0,0 +1,37 @@
+<?php
+
+final class PhabricatorConfigKeySchema
+ extends PhabricatorConfigStorageSchema {
+
+ private $columnNames;
+
+ public function setColumnNames(array $column_names) {
+ $this->columnNames = $column_names;
+ return $this;
+ }
+
+ public function getColumnNames() {
+ return $this->columnNames;
+ }
+
+ protected function getSubschemata() {
+ return array();
+ }
+
+ public function compareToSimilarSchema(
+ PhabricatorConfigStorageSchema $expect) {
+
+ $issues = array();
+ if ($this->getColumnNames() !== $expect->getColumnNames()) {
+ $issues[] = self::ISSUE_KEYCOLUMNS;
+ }
+
+ return $issues;
+ }
+
+ public function newEmptyClone() {
+ $clone = clone $this;
+ return $clone;
+ }
+
+}
diff --git a/src/applications/config/schema/PhabricatorConfigSchemaQuery.php b/src/applications/config/schema/PhabricatorConfigSchemaQuery.php
--- a/src/applications/config/schema/PhabricatorConfigSchemaQuery.php
+++ b/src/applications/config/schema/PhabricatorConfigSchemaQuery.php
@@ -47,6 +47,41 @@
$databases);
$database_info = ipull($database_info, null, 'SCHEMA_NAME');
+ $sql = array();
+ foreach ($tables as $table) {
+ $sql[] = qsprintf(
+ $conn,
+ '(TABLE_SCHEMA = %s AND TABLE_NAME = %s)',
+ $table['TABLE_SCHEMA'],
+ $table['TABLE_NAME']);
+ }
+
+ if ($sql) {
+ $column_info = queryfx_all(
+ $conn,
+ 'SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME,
+ COLLATION_NAME, COLUMN_TYPE, IS_NULLABLE
+ FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE (%Q)',
+ '('.implode(') OR (', $sql).')');
+ $column_info = igroup($column_info, 'TABLE_SCHEMA');
+ } else {
+ $column_info = array();
+ }
+
+ if ($sql) {
+ $key_info = queryfx_all(
+ $conn,
+ 'SELECT CONSTRAINT_NAME, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME,
+ ORDINAL_POSITION, POSITION_IN_UNIQUE_CONSTRAINT
+ FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
+ WHERE (%Q)',
+ '('.implode(') OR (', $sql).')');
+ $key_info = igroup($key_info, 'TABLE_SCHEMA');
+ } else {
+ $key_info = array();
+ }
+
$server_schema = new PhabricatorConfigServerSchema();
$tables = igroup($tables, 'TABLE_SCHEMA');
@@ -58,6 +93,11 @@
->setCharacterSet($info['DEFAULT_CHARACTER_SET_NAME'])
->setCollation($info['DEFAULT_COLLATION_NAME']);
+ $database_column_info = idx($column_info, $database_name, array());
+ $database_column_info = igroup($database_column_info, 'TABLE_NAME');
+ $database_key_info = idx($key_info, $database_name, array());
+ $database_key_info = igroup($database_key_info, 'TABLE_NAME');
+
foreach ($database_tables as $table) {
$table_name = $table['TABLE_NAME'];
@@ -65,24 +105,29 @@
->setName($table_name)
->setCollation($table['TABLE_COLLATION']);
- $columns = queryfx_all(
- $conn,
- 'SELECT COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME, COLUMN_TYPE
- FROM INFORMATION_SCHEMA.COLUMNS
- WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s',
- $database_name,
- $table_name);
-
+ $columns = idx($database_column_info, $table_name, array());
foreach ($columns as $column) {
$column_schema = id(new PhabricatorConfigColumnSchema())
->setName($column['COLUMN_NAME'])
->setCharacterSet($column['CHARACTER_SET_NAME'])
->setCollation($column['COLLATION_NAME'])
- ->setColumnType($column['COLUMN_TYPE']);
+ ->setColumnType($column['COLUMN_TYPE'])
+ ->setNullable($column['IS_NULLABLE'] == 'YES');
$table_schema->addColumn($column_schema);
}
+ $key_parts = idx($database_key_info, $table_name, array());
+ $keys = igroup($key_parts, 'CONSTRAINT_NAME');
+ foreach ($keys as $key_name => $key_pieces) {
+ $key_pieces = isort($key_pieces, 'ORDINAL_POSITION');
+ $key_schema = id(new PhabricatorConfigKeySchema())
+ ->setName($key_name)
+ ->setColumnNames(ipull($key_pieces, 'COLUMN_NAME'));
+
+ $table_schema->addKey($key_schema);
+ }
+
$database_schema->addTable($table_schema);
}
@@ -190,6 +235,22 @@
$comp_table->addColumn($comp_column);
}
+
+ $all_keys =
+ $actual_table->getKeys() +
+ $expect_table->getKeys();
+ foreach ($all_keys as $key_name => $key_template) {
+ $actual_key = $actual_table->getKey($key_name);
+ $expect_key = $expect_table->getKey($key_name);
+
+ $issues = $this->compareSchemata($expect_key, $actual_key);
+
+ $comp_key = $key_template->newEmptyClone()
+ ->setIssues($issues);
+
+ $comp_table->addKey($comp_key);
+ }
+
$comp_database->addTable($comp_table);
}
$comp_server->addDatabase($comp_database);
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
@@ -7,6 +7,8 @@
const ISSUE_CHARSET = 'charset';
const ISSUE_COLLATION = 'collation';
const ISSUE_COLUMNTYPE = 'columntype';
+ const ISSUE_NULLABLE = 'nullable';
+ const ISSUE_KEYCOLUMNS = 'keycolumns';
const ISSUE_SUBWARN = 'subwarn';
const ISSUE_SUBFAIL = 'subfail';
@@ -98,6 +100,10 @@
return pht('Wrong Collation');
case self::ISSUE_COLUMNTYPE:
return pht('Wrong Column Type');
+ case self::ISSUE_NULLABLE:
+ return pht('Wrong Nullable Setting');
+ case self::ISSUE_KEYCOLUMNS:
+ return pht('Key on Wrong Columns');
case self::ISSUE_SUBWARN:
return pht('Subschemata Have Warnings');
case self::ISSUE_SUBFAIL:
@@ -119,6 +125,10 @@
return pht('This schema can use a better collation.');
case self::ISSUE_COLUMNTYPE:
return pht('This schema can use a better column type.');
+ case self::ISSUE_NULLABLE:
+ return pht('This schema has the wrong nullable setting.');
+ case self::ISSUE_KEYCOLUMNS:
+ return pht('This schema is on the wrong columns.');
case self::ISSUE_SUBWARN:
return pht('Subschemata have setup warnings.');
case self::ISSUE_SUBFAIL:
@@ -138,6 +148,7 @@
case self::ISSUE_COLLATION:
case self::ISSUE_COLUMNTYPE:
case self::ISSUE_SUBWARN:
+ case self::ISSUE_KEYCOLUMNS:
return self::STATUS_WARN;
default:
throw new Exception(pht('Unknown schema issue "%s"!', $issue));
diff --git a/src/applications/config/schema/PhabricatorConfigTableSchema.php b/src/applications/config/schema/PhabricatorConfigTableSchema.php
--- a/src/applications/config/schema/PhabricatorConfigTableSchema.php
+++ b/src/applications/config/schema/PhabricatorConfigTableSchema.php
@@ -5,6 +5,7 @@
private $collation;
private $columns = array();
+ private $keys = array();
public function addColumn(PhabricatorConfigColumnSchema $column) {
$key = $column->getName();
@@ -16,6 +17,16 @@
return $this;
}
+ public function addKey(PhabricatorConfigKeySchema $key) {
+ $name = $key->getName();
+ if (isset($this->keys[$name])) {
+ throw new Exception(
+ pht('Trying to add duplicate key "%s"!', $name));
+ }
+ $this->keys[$name] = $key;
+ return $this;
+ }
+
public function getColumns() {
return $this->columns;
}
@@ -24,8 +35,16 @@
return idx($this->getColumns(), $key);
}
+ public function getKeys() {
+ return $this->keys;
+ }
+
+ public function getKey($key) {
+ return idx($this->getKeys(), $key);
+ }
+
protected function getSubschemata() {
- return $this->getColumns();
+ return array_merge($this->getColumns(), $this->getKeys());
}
public function setCollation($collation) {
@@ -51,6 +70,7 @@
public function newEmptyClone() {
$clone = clone $this;
$clone->columns = array();
+ $clone->keys = array();
return $clone;
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Oct 20, 2:46 PM (4 w, 1 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6735099
Default Alt Text
D10499.id.diff (18 KB)
Attached To
Mode
D10499: Support key schemata and column nullability
Attached
Detach File
Event Timeline
Log In to Comment