[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class PhabricatorConfigSchemaQuery extends Phobject { 4 5 private $api; 6 7 public function setAPI(PhabricatorStorageManagementAPI $api) { 8 $this->api = $api; 9 return $this; 10 } 11 12 protected function getAPI() { 13 if (!$this->api) { 14 throw new Exception(pht('Call setAPI() before issuing a query!')); 15 } 16 return $this->api; 17 } 18 19 protected function getConn() { 20 return $this->getAPI()->getConn(null); 21 } 22 23 private function getDatabaseNames() { 24 $api = $this->getAPI(); 25 $patches = PhabricatorSQLPatchList::buildAllPatches(); 26 return $api->getDatabaseList( 27 $patches, 28 $only_living = true); 29 } 30 31 public function loadActualSchema() { 32 $databases = $this->getDatabaseNames(); 33 34 $conn = $this->getConn(); 35 $tables = queryfx_all( 36 $conn, 37 'SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_COLLATION 38 FROM INFORMATION_SCHEMA.TABLES 39 WHERE TABLE_SCHEMA IN (%Ls)', 40 $databases); 41 42 $database_info = queryfx_all( 43 $conn, 44 'SELECT SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME 45 FROM INFORMATION_SCHEMA.SCHEMATA 46 WHERE SCHEMA_NAME IN (%Ls)', 47 $databases); 48 $database_info = ipull($database_info, null, 'SCHEMA_NAME'); 49 50 $sql = array(); 51 foreach ($tables as $table) { 52 $sql[] = qsprintf( 53 $conn, 54 '(TABLE_SCHEMA = %s AND TABLE_NAME = %s)', 55 $table['TABLE_SCHEMA'], 56 $table['TABLE_NAME']); 57 } 58 59 if ($sql) { 60 $column_info = queryfx_all( 61 $conn, 62 'SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME, 63 COLLATION_NAME, COLUMN_TYPE, IS_NULLABLE, EXTRA 64 FROM INFORMATION_SCHEMA.COLUMNS 65 WHERE (%Q)', 66 '('.implode(') OR (', $sql).')'); 67 $column_info = igroup($column_info, 'TABLE_SCHEMA'); 68 } else { 69 $column_info = array(); 70 } 71 72 // NOTE: Tables like KEY_COLUMN_USAGE and TABLE_CONSTRAINTS only contain 73 // primary, unique, and foreign keys, so we can't use them here. We pull 74 // indexes later on using SHOW INDEXES. 75 76 $server_schema = new PhabricatorConfigServerSchema(); 77 78 $tables = igroup($tables, 'TABLE_SCHEMA'); 79 foreach ($tables as $database_name => $database_tables) { 80 $info = $database_info[$database_name]; 81 82 $database_schema = id(new PhabricatorConfigDatabaseSchema()) 83 ->setName($database_name) 84 ->setCharacterSet($info['DEFAULT_CHARACTER_SET_NAME']) 85 ->setCollation($info['DEFAULT_COLLATION_NAME']); 86 87 $database_column_info = idx($column_info, $database_name, array()); 88 $database_column_info = igroup($database_column_info, 'TABLE_NAME'); 89 90 foreach ($database_tables as $table) { 91 $table_name = $table['TABLE_NAME']; 92 93 $table_schema = id(new PhabricatorConfigTableSchema()) 94 ->setName($table_name) 95 ->setCollation($table['TABLE_COLLATION']); 96 97 $columns = idx($database_column_info, $table_name, array()); 98 foreach ($columns as $column) { 99 if (strpos($column['EXTRA'], 'auto_increment') === false) { 100 $auto_increment = false; 101 } else { 102 $auto_increment = true; 103 } 104 105 $column_schema = id(new PhabricatorConfigColumnSchema()) 106 ->setName($column['COLUMN_NAME']) 107 ->setCharacterSet($column['CHARACTER_SET_NAME']) 108 ->setCollation($column['COLLATION_NAME']) 109 ->setColumnType($column['COLUMN_TYPE']) 110 ->setNullable($column['IS_NULLABLE'] == 'YES') 111 ->setAutoIncrement($auto_increment); 112 113 $table_schema->addColumn($column_schema); 114 } 115 116 $key_parts = queryfx_all( 117 $conn, 118 'SHOW INDEXES FROM %T.%T', 119 $database_name, 120 $table_name); 121 $keys = igroup($key_parts, 'Key_name'); 122 foreach ($keys as $key_name => $key_pieces) { 123 $key_pieces = isort($key_pieces, 'Seq_in_index'); 124 $head = head($key_pieces); 125 126 // This handles string indexes which index only a prefix of a field. 127 $column_names = array(); 128 foreach ($key_pieces as $piece) { 129 $name = $piece['Column_name']; 130 if ($piece['Sub_part']) { 131 $name = $name.'('.$piece['Sub_part'].')'; 132 } 133 $column_names[] = $name; 134 } 135 136 $key_schema = id(new PhabricatorConfigKeySchema()) 137 ->setName($key_name) 138 ->setColumnNames($column_names) 139 ->setUnique(!$head['Non_unique']) 140 ->setIndexType($head['Index_type']); 141 142 $table_schema->addKey($key_schema); 143 } 144 145 $database_schema->addTable($table_schema); 146 } 147 148 $server_schema->addDatabase($database_schema); 149 } 150 151 return $server_schema; 152 } 153 154 public function loadExpectedSchema() { 155 $databases = $this->getDatabaseNames(); 156 $info = $this->getAPI()->getCharsetInfo(); 157 158 $specs = id(new PhutilSymbolLoader()) 159 ->setAncestorClass('PhabricatorConfigSchemaSpec') 160 ->loadObjects(); 161 162 $server_schema = new PhabricatorConfigServerSchema(); 163 foreach ($specs as $spec) { 164 $spec 165 ->setUTF8Charset( 166 $info[PhabricatorStorageManagementAPI::CHARSET_DEFAULT]) 167 ->setUTF8BinaryCollation( 168 $info[PhabricatorStorageManagementAPI::COLLATE_TEXT]) 169 ->setUTF8SortingCollation( 170 $info[PhabricatorStorageManagementAPI::COLLATE_SORT]) 171 ->setServer($server_schema) 172 ->buildSchemata($server_schema); 173 } 174 175 return $server_schema; 176 } 177 178 public function buildComparisonSchema( 179 PhabricatorConfigServerSchema $expect, 180 PhabricatorConfigServerSchema $actual) { 181 182 $comp_server = $actual->newEmptyClone(); 183 184 $all_databases = $actual->getDatabases() + $expect->getDatabases(); 185 foreach ($all_databases as $database_name => $database_template) { 186 $actual_database = $actual->getDatabase($database_name); 187 $expect_database = $expect->getDatabase($database_name); 188 189 $issues = $this->compareSchemata($expect_database, $actual_database); 190 191 $comp_database = $database_template->newEmptyClone() 192 ->setIssues($issues); 193 194 if (!$actual_database) { 195 $actual_database = $expect_database->newEmptyClone(); 196 } 197 if (!$expect_database) { 198 $expect_database = $actual_database->newEmptyClone(); 199 } 200 201 $all_tables = 202 $actual_database->getTables() + 203 $expect_database->getTables(); 204 foreach ($all_tables as $table_name => $table_template) { 205 $actual_table = $actual_database->getTable($table_name); 206 $expect_table = $expect_database->getTable($table_name); 207 208 $issues = $this->compareSchemata($expect_table, $actual_table); 209 210 $comp_table = $table_template->newEmptyClone() 211 ->setIssues($issues); 212 213 if (!$actual_table) { 214 $actual_table = $expect_table->newEmptyClone(); 215 } 216 if (!$expect_table) { 217 $expect_table = $actual_table->newEmptyClone(); 218 } 219 220 $all_columns = 221 $actual_table->getColumns() + 222 $expect_table->getColumns(); 223 foreach ($all_columns as $column_name => $column_template) { 224 $actual_column = $actual_table->getColumn($column_name); 225 $expect_column = $expect_table->getColumn($column_name); 226 227 $issues = $this->compareSchemata($expect_column, $actual_column); 228 229 $comp_column = $column_template->newEmptyClone() 230 ->setIssues($issues); 231 232 $comp_table->addColumn($comp_column); 233 } 234 235 $all_keys = 236 $actual_table->getKeys() + 237 $expect_table->getKeys(); 238 foreach ($all_keys as $key_name => $key_template) { 239 $actual_key = $actual_table->getKey($key_name); 240 $expect_key = $expect_table->getKey($key_name); 241 242 $issues = $this->compareSchemata($expect_key, $actual_key); 243 244 $comp_key = $key_template->newEmptyClone() 245 ->setIssues($issues); 246 247 $comp_table->addKey($comp_key); 248 } 249 250 $comp_database->addTable($comp_table); 251 } 252 $comp_server->addDatabase($comp_database); 253 } 254 255 return $comp_server; 256 } 257 258 private function compareSchemata( 259 PhabricatorConfigStorageSchema $expect = null, 260 PhabricatorConfigStorageSchema $actual = null) { 261 262 $expect_is_key = ($expect instanceof PhabricatorConfigKeySchema); 263 $actual_is_key = ($actual instanceof PhabricatorConfigKeySchema); 264 265 if ($expect_is_key || $actual_is_key) { 266 $missing_issue = PhabricatorConfigStorageSchema::ISSUE_MISSINGKEY; 267 $surplus_issue = PhabricatorConfigStorageSchema::ISSUE_SURPLUSKEY; 268 } else { 269 $missing_issue = PhabricatorConfigStorageSchema::ISSUE_MISSING; 270 $surplus_issue = PhabricatorConfigStorageSchema::ISSUE_SURPLUS; 271 } 272 273 if (!$expect && !$actual) { 274 throw new Exception(pht('Can not compare two missing schemata!')); 275 } else if ($expect && !$actual) { 276 $issues = array($missing_issue); 277 } else if ($actual && !$expect) { 278 $issues = array($surplus_issue); 279 } else { 280 $issues = $actual->compareTo($expect); 281 } 282 283 return $issues; 284 } 285 286 287 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |