[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/config/schema/ -> PhabricatorConfigSchemaQuery.php (source)

   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  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1