@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

at recaptime-dev/main 464 lines 13 kB view raw
1<?php 2 3abstract class PhabricatorConfigSchemaSpec extends Phobject { 4 5 private $server; 6 private $utf8Charset; 7 private $utf8BinaryCollation; 8 private $utf8SortingCollation; 9 10 const DATATYPE_UNKNOWN = '<unknown>'; 11 12 public function setUTF8SortingCollation($utf8_sorting_collation) { 13 $this->utf8SortingCollation = $utf8_sorting_collation; 14 return $this; 15 } 16 17 public function getUTF8SortingCollation() { 18 return $this->utf8SortingCollation; 19 } 20 21 public function setUTF8BinaryCollation($utf8_binary_collation) { 22 $this->utf8BinaryCollation = $utf8_binary_collation; 23 return $this; 24 } 25 26 public function getUTF8BinaryCollation() { 27 return $this->utf8BinaryCollation; 28 } 29 30 public function setUTF8Charset($utf8_charset) { 31 $this->utf8Charset = $utf8_charset; 32 return $this; 33 } 34 35 public function getUTF8Charset() { 36 return $this->utf8Charset; 37 } 38 39 public function setServer(PhabricatorConfigServerSchema $server) { 40 $this->server = $server; 41 return $this; 42 } 43 44 public function getServer() { 45 return $this->server; 46 } 47 48 abstract public function buildSchemata(); 49 50 protected function buildLiskObjectSchema(PhabricatorLiskDAO $object) { 51 $index_options = array(); 52 53 $persistence = $object->getSchemaPersistence(); 54 if ($persistence !== null) { 55 $index_options['persistence'] = $persistence; 56 } 57 58 $this->buildRawSchema( 59 $object->getApplicationName(), 60 $object->getTableName(), 61 $object->getSchemaColumns(), 62 $object->getSchemaKeys(), 63 $index_options); 64 } 65 66 protected function buildFerretIndexSchema(PhabricatorFerretEngine $engine) { 67 $index_options = array( 68 'persistence' => PhabricatorConfigTableSchema::PERSISTENCE_INDEX, 69 ); 70 71 $this->buildRawSchema( 72 $engine->getApplicationName(), 73 $engine->getDocumentTableName(), 74 $engine->getDocumentSchemaColumns(), 75 $engine->getDocumentSchemaKeys(), 76 $index_options); 77 78 $this->buildRawSchema( 79 $engine->getApplicationName(), 80 $engine->getFieldTableName(), 81 $engine->getFieldSchemaColumns(), 82 $engine->getFieldSchemaKeys(), 83 $index_options); 84 85 $this->buildRawSchema( 86 $engine->getApplicationName(), 87 $engine->getNgramsTableName(), 88 $engine->getNgramsSchemaColumns(), 89 $engine->getNgramsSchemaKeys(), 90 $index_options); 91 92 // NOTE: The common ngrams table is not marked as an index table. It is 93 // tiny and persisting it across a restore saves us a lot of work garbage 94 // collecting common ngrams from the index after it gets built. 95 96 $this->buildRawSchema( 97 $engine->getApplicationName(), 98 $engine->getCommonNgramsTableName(), 99 $engine->getCommonNgramsSchemaColumns(), 100 $engine->getCommonNgramsSchemaKeys()); 101 } 102 103 protected function buildRawSchema( 104 $database_name, 105 $table_name, 106 array $columns, 107 array $keys, 108 array $options = array()) { 109 110 PhutilTypeSpec::checkMap( 111 $options, 112 array( 113 'persistence' => 'optional string', 114 )); 115 116 $database = $this->getDatabase($database_name); 117 118 $table = $this->newTable($table_name); 119 120 if (PhabricatorSearchDocument::isInnoDBFulltextEngineAvailable()) { 121 $fulltext_engine = 'InnoDB'; 122 } else { 123 $fulltext_engine = 'MyISAM'; 124 } 125 126 foreach ($columns as $name => $type) { 127 if ($type === null) { 128 continue; 129 } 130 131 $details = $this->getDetailsForDataType($type); 132 133 $column_type = $details['type']; 134 $charset = $details['charset']; 135 $collation = $details['collation']; 136 $nullable = $details['nullable']; 137 $auto = $details['auto']; 138 139 $column = $this->newColumn($name) 140 ->setDataType($type) 141 ->setColumnType($column_type) 142 ->setCharacterSet($charset) 143 ->setCollation($collation) 144 ->setNullable($nullable) 145 ->setAutoIncrement($auto); 146 147 // If this table has any FULLTEXT fields, we expect it to use the best 148 // available FULLTEXT engine, which may not be InnoDB. 149 switch ($type) { 150 case 'fulltext': 151 case 'fulltext?': 152 $table->setEngine($fulltext_engine); 153 break; 154 } 155 156 $table->addColumn($column); 157 } 158 159 foreach ($keys as $key_name => $key_spec) { 160 if ($key_spec === null) { 161 // This is a subclass removing a key which Lisk expects. 162 continue; 163 } 164 165 $key = $this->newKey($key_name) 166 ->setColumnNames(idx($key_spec, 'columns', array())); 167 168 $key->setUnique((bool)idx($key_spec, 'unique')); 169 $key->setIndexType(idx($key_spec, 'type', 'BTREE')); 170 171 $table->addKey($key); 172 } 173 174 $persistence_type = idx($options, 'persistence'); 175 if ($persistence_type !== null) { 176 $table->setPersistenceType($persistence_type); 177 } 178 179 $database->addTable($table); 180 } 181 182 protected function buildEdgeSchemata(PhabricatorLiskDAO $object) { 183 $this->buildRawSchema( 184 $object->getApplicationName(), 185 PhabricatorEdgeConfig::TABLE_NAME_EDGE, 186 array( 187 'src' => 'phid', 188 'type' => 'uint32', 189 'dst' => 'phid', 190 'dateCreated' => 'epoch', 191 'seq' => 'uint32', 192 'dataID' => 'id?', 193 ), 194 array( 195 'PRIMARY' => array( 196 'columns' => array('src', 'type', 'dst'), 197 'unique' => true, 198 ), 199 'src' => array( 200 'columns' => array('src', 'type', 'dateCreated', 'seq'), 201 ), 202 'key_dst' => array( 203 'columns' => array('dst', 'type', 'src'), 204 'unique' => true, 205 ), 206 )); 207 208 $this->buildRawSchema( 209 $object->getApplicationName(), 210 PhabricatorEdgeConfig::TABLE_NAME_EDGEDATA, 211 array( 212 'id' => 'auto', 213 'data' => 'text', 214 ), 215 array( 216 'PRIMARY' => array( 217 'columns' => array('id'), 218 'unique' => true, 219 ), 220 )); 221 } 222 223 protected function getDatabase($name) { 224 $server = $this->getServer(); 225 226 $database = $server->getDatabase($this->getNamespacedDatabase($name)); 227 if (!$database) { 228 $database = $this->newDatabase($name); 229 $server->addDatabase($database); 230 } 231 232 return $database; 233 } 234 235 protected function newDatabase($name) { 236 return id(new PhabricatorConfigDatabaseSchema()) 237 ->setName($this->getNamespacedDatabase($name)) 238 ->setCharacterSet($this->getUTF8Charset()) 239 ->setCollation($this->getUTF8BinaryCollation()); 240 } 241 242 protected function getNamespacedDatabase($name) { 243 $namespace = PhabricatorLiskDAO::getStorageNamespace(); 244 return $namespace.'_'.$name; 245 } 246 247 protected function newTable($name) { 248 return id(new PhabricatorConfigTableSchema()) 249 ->setName($name) 250 ->setCollation($this->getUTF8BinaryCollation()) 251 ->setEngine('InnoDB'); 252 } 253 254 protected function newColumn($name) { 255 return id(new PhabricatorConfigColumnSchema()) 256 ->setName($name); 257 } 258 259 protected function newKey($name) { 260 return id(new PhabricatorConfigKeySchema()) 261 ->setName($name); 262 } 263 264 public function getMaximumByteLengthForDataType($data_type) { 265 $info = $this->getDetailsForDataType($data_type); 266 return idx($info, 'bytes'); 267 } 268 269 private function getDetailsForDataType($data_type) { 270 $column_type = null; 271 $charset = null; 272 $collation = null; 273 $auto = false; 274 $bytes = null; 275 276 // If the type ends with "?", make the column nullable. 277 $nullable = false; 278 if (preg_match('/\?$/', $data_type)) { 279 $nullable = true; 280 $data_type = substr($data_type, 0, -1); 281 } 282 283 // NOTE: MySQL allows fragments like "VARCHAR(32) CHARACTER SET binary", 284 // but just interprets that to mean "VARBINARY(32)". The fragment is 285 // totally disallowed in a MODIFY statement vs a CREATE TABLE statement. 286 287 $is_binary = ($this->getUTF8Charset() == 'binary'); 288 $matches = null; 289 $pattern = '/^(fulltext|sort|text|char)(\d+)?\z/'; 290 if (preg_match($pattern, $data_type, $matches)) { 291 292 // Limit the permitted column lengths under the theory that it would 293 // be nice to eventually reduce this to a small set of standard lengths. 294 295 static $valid_types = array( 296 'text255' => true, 297 'text160' => true, 298 'text128' => true, 299 'text64' => true, 300 'text40' => true, 301 'text32' => true, 302 'text20' => true, 303 'text16' => true, 304 'text12' => true, 305 'text8' => true, 306 'text4' => true, 307 'text' => true, 308 'char3' => true, 309 'sort255' => true, 310 'sort128' => true, 311 'sort64' => true, 312 'sort32' => true, 313 'sort' => true, 314 'fulltext' => true, 315 ); 316 317 if (empty($valid_types[$data_type])) { 318 throw new Exception(pht('Unknown column type "%s"!', $data_type)); 319 } 320 321 $type = $matches[1]; 322 $size = idx($matches, 2); 323 324 if ($size) { 325 $bytes = $size; 326 } 327 328 switch ($type) { 329 case 'text': 330 if ($is_binary) { 331 if ($size) { 332 $column_type = 'varbinary('.$size.')'; 333 } else { 334 $column_type = 'longblob'; 335 } 336 } else { 337 if ($size) { 338 $column_type = 'varchar('.$size.')'; 339 } else { 340 $column_type = 'longtext'; 341 } 342 } 343 break; 344 case 'sort': 345 if ($size) { 346 $column_type = 'varchar('.$size.')'; 347 } else { 348 $column_type = 'longtext'; 349 } 350 break; 351 case 'fulltext': 352 // MySQL (at least, under MyISAM) refuses to create a FULLTEXT index 353 // on a LONGBLOB column. We'd also lose case insensitivity in search. 354 // Force this column to utf8 collation. This will truncate results 355 // with 4-byte UTF characters in their text, but work reasonably in 356 // the majority of cases. 357 $column_type = 'longtext'; 358 break; 359 case 'char': 360 $column_type = 'char('.$size.')'; 361 break; 362 } 363 364 switch ($type) { 365 case 'text': 366 case 'char': 367 if ($is_binary) { 368 // We leave collation and character set unspecified in order to 369 // generate valid SQL. 370 } else { 371 $charset = $this->getUTF8Charset(); 372 $collation = $this->getUTF8BinaryCollation(); 373 } 374 break; 375 case 'sort': 376 case 'fulltext': 377 if ($is_binary) { 378 $charset = 'utf8'; 379 } else { 380 $charset = $this->getUTF8Charset(); 381 } 382 $collation = $this->getUTF8SortingCollation(); 383 break; 384 } 385 } else { 386 switch ($data_type) { 387 case 'auto': 388 $column_type = 'int(10) unsigned'; 389 $auto = true; 390 break; 391 case 'auto64': 392 $column_type = 'bigint(20) unsigned'; 393 $auto = true; 394 break; 395 case 'id': 396 case 'epoch': 397 case 'uint32': 398 $column_type = 'int(10) unsigned'; 399 break; 400 case 'sint32': 401 $column_type = 'int(10)'; 402 break; 403 case 'id64': 404 case 'uint64': 405 $column_type = 'bigint(20) unsigned'; 406 break; 407 case 'sint64': 408 $column_type = 'bigint(20)'; 409 break; 410 case 'phid': 411 case 'policy': 412 case 'hashpath64': 413 case 'ipaddress': 414 $column_type = 'varbinary(64)'; 415 break; 416 case 'bytes64': 417 $column_type = 'binary(64)'; 418 break; 419 case 'bytes40': 420 $column_type = 'binary(40)'; 421 break; 422 case 'bytes32': 423 $column_type = 'binary(32)'; 424 break; 425 case 'bytes20': 426 $column_type = 'binary(20)'; 427 break; 428 case 'bytes12': 429 $column_type = 'binary(12)'; 430 break; 431 case 'bytes4': 432 $column_type = 'binary(4)'; 433 break; 434 case 'bytes': 435 $column_type = 'longblob'; 436 break; 437 case 'bool': 438 $column_type = 'tinyint(1)'; 439 break; 440 case 'double': 441 $column_type = 'double'; 442 break; 443 case 'date': 444 $column_type = 'date'; 445 break; 446 default: 447 $column_type = self::DATATYPE_UNKNOWN; 448 $charset = self::DATATYPE_UNKNOWN; 449 $collation = self::DATATYPE_UNKNOWN; 450 break; 451 } 452 } 453 454 return array( 455 'type' => $column_type, 456 'charset' => $charset, 457 'collation' => $collation, 458 'nullable' => $nullable, 459 'auto' => $auto, 460 'bytes' => $bytes, 461 ); 462 } 463 464}