@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

Fix almost all remaining schemata issues

Summary:
Ref T1191. This fixes nearly every remaining blocker for utf8mb4 -- primarily, overlong keys.

Remaining issue is https://secure.phabricator.com/T1191#77467

Test Plan: I'll annotate inline.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley, hach-que

Maniphest Tasks: T6099, T6129, T6133, T6134, T6150, T6148, T6147, T6146, T6105, T1191

Differential Revision: https://secure.phabricator.com/D10601

+181 -90
+1 -1
src/applications/auth/storage/PhabricatorAuthProviderConfig.php
··· 32 32 self::CONFIG_COLUMN_SCHEMA => array( 33 33 'isEnabled' => 'bool', 34 34 'providerClass' => 'text128', 35 - 'providerType' => 'text64', 35 + 'providerType' => 'text32', 36 36 'providerDomain' => 'text128', 37 37 'shouldAllowLogin' => 'bool', 38 38 'shouldAllowRegistration' => 'bool',
+1 -1
src/applications/conduit/storage/PhabricatorConduitMethodCallLog.php
··· 15 15 self::CONFIG_COLUMN_SCHEMA => array( 16 16 'id' => 'id64', 17 17 'connectionID' => 'id64?', 18 - 'method' => 'text255', 18 + 'method' => 'text64', 19 19 'error' => 'text255', 20 20 'duration' => 'uint64', 21 21 'callerPHID' => 'phid?',
-2
src/applications/config/controller/PhabricatorConfigDatabaseController.php
··· 3 3 abstract class PhabricatorConfigDatabaseController 4 4 extends PhabricatorConfigController { 5 5 6 - const MAX_INNODB_KEY_LENGTH = 767; 7 - 8 6 protected function buildSchemaQuery() { 9 7 $conf = PhabricatorEnv::newObjectFromConfig( 10 8 'mysql.configuration-provider',
+2 -1
src/applications/config/controller/PhabricatorConfigDatabaseStatusController.php
··· 281 281 $nullable_issue = PhabricatorConfigStorageSchema::ISSUE_NULLABLE; 282 282 $unique_issue = PhabricatorConfigStorageSchema::ISSUE_UNIQUE; 283 283 $columns_issue = PhabricatorConfigStorageSchema::ISSUE_KEYCOLUMNS; 284 + $longkey_issue = PhabricatorConfigStorageSchema::ISSUE_LONGKEY; 284 285 285 286 $database = $comp->getDatabase($database_name); 286 287 if (!$database) { ··· 392 393 if ($size) { 393 394 $size_formatted = $this->renderAttr( 394 395 $size, 395 - ($size > self::MAX_INNODB_KEY_LENGTH)); 396 + $key->hasIssue($longkey_issue)); 396 397 } 397 398 398 399 $key_rows[] = array(
+3 -3
src/applications/config/schema/PhabricatorConfigColumnSchema.php
··· 62 62 $type = $this->getColumnType(); 63 63 64 64 $matches = null; 65 - if (preg_match('/^varchar\((\d+)\)$/', $type, $matches)) { 65 + if (preg_match('/^(?:var)?char\((\d+)\)$/', $type, $matches)) { 66 66 // For utf8mb4, each character requires 4 bytes. 67 67 $size = (int)$matches[1]; 68 68 if ($prefix && $prefix < $size) { ··· 72 72 } 73 73 74 74 $matches = null; 75 - if (preg_match('/^char\((\d+)\)$/', $type, $matches)) { 76 - // We use char() only for fixed-length binary data, so its size 75 + if (preg_match('/^(?:var)?binary\((\d+)\)$/', $type, $matches)) { 76 + // binary()/varbinary() store fixed-length binary data, so their size 77 77 // is always the column size. 78 78 $size = (int)$matches[1]; 79 79 if ($prefix && $prefix < $size) {
+54
src/applications/config/schema/PhabricatorConfigKeySchema.php
··· 3 3 final class PhabricatorConfigKeySchema 4 4 extends PhabricatorConfigStorageSchema { 5 5 6 + const MAX_INNODB_KEY_LENGTH = 767; 7 + 6 8 private $columnNames; 7 9 private $unique; 10 + private $table; 11 + private $indexType; 12 + 13 + public function setIndexType($index_type) { 14 + $this->indexType = $index_type; 15 + return $this; 16 + } 17 + 18 + public function getIndexType() { 19 + return $this->indexType; 20 + } 21 + 22 + public function setProperty($property) { 23 + $this->property = $property; 24 + return $this; 25 + } 26 + 27 + public function getProperty() { 28 + return $this->property; 29 + } 8 30 9 31 public function setUnique($unique) { 10 32 $this->unique = $unique; ··· 15 37 return $this->unique; 16 38 } 17 39 40 + public function setTable(PhabricatorConfigTableSchema $table) { 41 + $this->table = $table; 42 + return $this; 43 + } 44 + 45 + public function getTable() { 46 + return $this->table; 47 + } 48 + 18 49 public function setColumnNames(array $column_names) { 19 50 $this->columnNames = array_values($column_names); 20 51 return $this; ··· 37 68 } 38 69 } 39 70 71 + public function getKeyByteLength() { 72 + $size = 0; 73 + foreach ($this->getColumnNames() as $column_spec) { 74 + list($column_name, $prefix) = $this->getKeyColumnAndPrefix($column_spec); 75 + $column = $this->getTable()->getColumn($column_name); 76 + if (!$column) { 77 + $size = 0; 78 + break; 79 + } 80 + $size += $column->getKeyByteLength($prefix); 81 + } 82 + 83 + return $size; 84 + } 85 + 40 86 public function compareToSimilarSchema( 41 87 PhabricatorConfigStorageSchema $expect) { 42 88 ··· 49 95 $issues[] = self::ISSUE_UNIQUE; 50 96 } 51 97 98 + // A fulltext index can be of any length. 99 + if ($this->getIndexType() != 'FULLTEXT') { 100 + if ($this->getKeyByteLength() > self::MAX_INNODB_KEY_LENGTH) { 101 + $issues[] = self::ISSUE_LONGKEY; 102 + } 103 + } 104 + 52 105 return $issues; 53 106 } 54 107 55 108 public function newEmptyClone() { 56 109 $clone = clone $this; 110 + $this->table = null; 57 111 return $clone; 58 112 } 59 113
+2 -1
src/applications/config/schema/PhabricatorConfigSchemaQuery.php
··· 129 129 $key_schema = id(new PhabricatorConfigKeySchema()) 130 130 ->setName($key_name) 131 131 ->setColumnNames($column_names) 132 - ->setUnique(!$head['Non_unique']); 132 + ->setUnique(!$head['Non_unique']) 133 + ->setIndexType($head['Index_type']); 133 134 134 135 $table_schema->addKey($key_schema); 135 136 }
+1
src/applications/config/schema/PhabricatorConfigSchemaSpec.php
··· 114 114 ->setColumnNames(idx($key_spec, 'columns', array())); 115 115 116 116 $key->setUnique((bool)idx($key_spec, 'unique')); 117 + $key->setIndexType(idx($key_spec, 'type', 'BTREE')); 117 118 118 119 $table->addKey($key); 119 120 }
+7 -1
src/applications/config/schema/PhabricatorConfigStorageSchema.php
··· 12 12 const ISSUE_NULLABLE = 'nullable'; 13 13 const ISSUE_KEYCOLUMNS = 'keycolumns'; 14 14 const ISSUE_UNIQUE = 'unique'; 15 + const ISSUE_LONGKEY = 'longkey'; 15 16 const ISSUE_SUBWARN = 'subwarn'; 16 17 const ISSUE_SUBFAIL = 'subfail'; 17 18 ··· 117 118 return pht('Key on Wrong Columns'); 118 119 case self::ISSUE_UNIQUE: 119 120 return pht('Key has Wrong Uniqueness'); 121 + case self::ISSUE_LONGKEY: 122 + return pht('Key is Too Long'); 120 123 case self::ISSUE_SUBWARN: 121 124 return pht('Subschemata Have Warnings'); 122 125 case self::ISSUE_SUBFAIL: ··· 145 148 case self::ISSUE_NULLABLE: 146 149 return pht('This schema has the wrong nullable setting.'); 147 150 case self::ISSUE_KEYCOLUMNS: 148 - return pht('This schema is on the wrong columns.'); 151 + return pht('This key is on the wrong columns.'); 149 152 case self::ISSUE_UNIQUE: 150 153 return pht('This key has the wrong uniqueness setting.'); 154 + case self::ISSUE_LONGKEY: 155 + return pht('This key is too long for utf8mb4.'); 151 156 case self::ISSUE_SUBWARN: 152 157 return pht('Subschemata have setup warnings.'); 153 158 case self::ISSUE_SUBFAIL: ··· 172 177 case self::ISSUE_SURPLUSKEY: 173 178 case self::ISSUE_UNIQUE: 174 179 case self::ISSUE_KEYCOLUMNS: 180 + case self::ISSUE_LONGKEY: 175 181 return self::STATUS_WARN; 176 182 default: 177 183 throw new Exception(pht('Unknown schema issue "%s"!', $issue));
+7 -1
src/applications/config/schema/PhabricatorConfigTableSchema.php
··· 23 23 throw new Exception( 24 24 pht('Trying to add duplicate key "%s"!', $name)); 25 25 } 26 + $key->setTable($this); 26 27 $this->keys[$name] = $key; 27 28 return $this; 28 29 } ··· 44 45 } 45 46 46 47 protected function getSubschemata() { 47 - return array_merge($this->getColumns(), $this->getKeys()); 48 + // NOTE: Keys and columns may have the same name, so make sure we return 49 + // everything. 50 + 51 + return array_merge( 52 + array_values($this->columns), 53 + array_values($this->keys)); 48 54 } 49 55 50 56 public function setCollation($collation) {
+1 -1
src/applications/differential/storage/DifferentialDiffProperty.php
··· 12 12 'data' => self::SERIALIZATION_JSON, 13 13 ), 14 14 self::CONFIG_COLUMN_SCHEMA => array( 15 - 'name' => 'text255', 15 + 'name' => 'text128', 16 16 ), 17 17 self::CONFIG_KEY_SCHEMA => array( 18 18 'diffID' => array(
+1 -1
src/applications/diviner/storage/DivinerLiveSymbol.php
··· 69 69 ), 70 70 ), 71 71 'name' => array( 72 - 'columns' => array('name'), 72 + 'columns' => array('name(64)'), 73 73 ), 74 74 'key_slug' => array( 75 75 'columns' => array('titleSlugHash'),
+1 -1
src/applications/files/storage/PhabricatorTransformedFile.php
··· 9 9 public function getConfiguration() { 10 10 return array( 11 11 self::CONFIG_COLUMN_SCHEMA => array( 12 - 'transform' => 'text255', 12 + 'transform' => 'text128', 13 13 ), 14 14 self::CONFIG_KEY_SCHEMA => array( 15 15 'originalPHID' => array(
+7 -14
src/applications/herald/controller/HeraldRuleController.php
··· 322 322 $rule->attachActions($actions); 323 323 324 324 if (!$errors) { 325 - try { 325 + $edit_action = $rule->getID() ? 'edit' : 'create'; 326 326 327 - $edit_action = $rule->getID() ? 'edit' : 'create'; 328 - 329 - $rule->openTransaction(); 330 - $rule->save(); 331 - $rule->saveConditions($conditions); 332 - $rule->saveActions($actions); 333 - $rule->logEdit($request->getUser()->getPHID(), $edit_action); 334 - $rule->saveTransaction(); 335 - 336 - } catch (AphrontDuplicateKeyQueryException $ex) { 337 - $e_name = pht('Not Unique'); 338 - $errors[] = pht('Rule name is not unique. Choose a unique name.'); 339 - } 327 + $rule->openTransaction(); 328 + $rule->save(); 329 + $rule->saveConditions($conditions); 330 + $rule->saveActions($actions); 331 + $rule->logEdit($request->getUser()->getPHID(), $edit_action); 332 + $rule->saveTransaction(); 340 333 } 341 334 342 335 return array($e_name, $errors);
+4 -10
src/applications/herald/storage/HeraldRule.php
··· 36 36 'contentType' => 'text255', 37 37 'mustMatchAll' => 'bool', 38 38 'configVersion' => 'uint32', 39 - 'ruleType' => 'text255', 39 + 'ruleType' => 'text32', 40 40 'isDisabled' => 'uint32', 41 41 'triggerObjectPHID' => 'phid?', 42 42 ··· 45 45 'repetitionPolicy' => 'uint32?', 46 46 ), 47 47 self::CONFIG_KEY_SCHEMA => array( 48 - 'key_phid' => null, 49 - 'phid' => array( 50 - 'columns' => array('phid'), 51 - 'unique' => true, 52 - ), 53 - 'authorPHID' => array( 54 - 'columns' => array('authorPHID', 'name'), 55 - 'unique' => true, 48 + 'key_author' => array( 49 + 'columns' => array('authorPHID'), 56 50 ), 57 - 'IDX_RULE_TYPE' => array( 51 + 'key_ruletype' => array( 58 52 'columns' => array('ruleType'), 59 53 ), 60 54 'key_trigger' => array(
+1
src/applications/herald/storage/transcript/HeraldTranscript.php
··· 106 106 'host' => 'text255', 107 107 'duration' => 'double', 108 108 'dryRun' => 'bool', 109 + 'garbageCollected' => 'bool', 109 110 ), 110 111 self::CONFIG_KEY_SCHEMA => array( 111 112 'key_phid' => null,
+1 -1
src/applications/macro/storage/PhabricatorFileImageMacro.php
··· 44 44 return array( 45 45 self::CONFIG_AUX_PHID => true, 46 46 self::CONFIG_COLUMN_SCHEMA => array( 47 - 'name' => 'text255', 47 + 'name' => 'text128', 48 48 'authorPHID' => 'phid?', 49 49 'isDisabled' => 'bool', 50 50 'audioPHID' => 'phid?',
+2 -2
src/applications/mailinglists/storage/PhabricatorMetaMTAMailingList.php
··· 18 18 return array( 19 19 self::CONFIG_AUX_PHID => true, 20 20 self::CONFIG_COLUMN_SCHEMA => array( 21 - 'name' => 'text255', 22 - 'email' => 'text255', 21 + 'name' => 'text128', 22 + 'email' => 'text128', 23 23 'uri' => 'text255?', 24 24 ), 25 25 self::CONFIG_KEY_SCHEMA => array(
+1 -1
src/applications/metamta/storage/PhabricatorMetaMTAMail.php
··· 33 33 'parameters' => self::SERIALIZATION_JSON, 34 34 ), 35 35 self::CONFIG_COLUMN_SCHEMA => array( 36 - 'status' => 'text255', 36 + 'status' => 'text32', 37 37 'relatedPHID' => 'phid?', 38 38 39 39 // T6203/NULLABILITY
+1 -1
src/applications/owners/storage/PhabricatorOwnersPackage.php
··· 39 39 self::CONFIG_TIMESTAMPS => false, 40 40 self::CONFIG_AUX_PHID => true, 41 41 self::CONFIG_COLUMN_SCHEMA => array( 42 - 'name' => 'text255', 42 + 'name' => 'text128', 43 43 'originalName' => 'text255', 44 44 'description' => 'text', 45 45 'primaryOwnerPHID' => 'phid?',
+1 -1
src/applications/people/storage/PhabricatorExternalAccount.php
··· 44 44 'accountType' => 'text16', 45 45 'accountDomain' => 'text64', 46 46 'accountSecret' => 'text?', 47 - 'accountID' => 'text160', 47 + 'accountID' => 'text64', 48 48 'displayName' => 'text255?', 49 49 'username' => 'text255?', 50 50 'realName' => 'text255?',
+1 -1
src/applications/people/storage/PhabricatorUserSchemaSpec.php
··· 26 26 ), 27 27 array( 28 28 'token' => array( 29 - 'columns' => array('token'), 29 + 'columns' => array('token(128)'), 30 30 ), 31 31 'userID' => array( 32 32 'columns' => array('userID'),
+1 -1
src/applications/phame/storage/PhameBlog.php
··· 30 30 self::CONFIG_COLUMN_SCHEMA => array( 31 31 'name' => 'text64', 32 32 'description' => 'text', 33 - 'domain' => 'text255?', 33 + 'domain' => 'text128?', 34 34 35 35 // T6203/NULLABILITY 36 36 // These policies should always be non-null.
+1 -1
src/applications/phragment/storage/PhragmentFragment.php
··· 15 15 return array( 16 16 self::CONFIG_AUX_PHID => true, 17 17 self::CONFIG_COLUMN_SCHEMA => array( 18 - 'path' => 'text255', 18 + 'path' => 'text128', 19 19 'depth' => 'uint32', 20 20 'latestVersionPHID' => 'phid?', 21 21 ),
+1 -1
src/applications/phragment/storage/PhragmentSnapshot.php
··· 12 12 return array( 13 13 self::CONFIG_AUX_PHID => true, 14 14 self::CONFIG_COLUMN_SCHEMA => array( 15 - 'name' => 'text255', 15 + 'name' => 'text128', 16 16 ), 17 17 self::CONFIG_KEY_SCHEMA => array( 18 18 'key_name' => array(
+1 -1
src/applications/phriction/storage/PhrictionContent.php
··· 51 51 'columns' => array('authorPHID'), 52 52 ), 53 53 'slug' => array( 54 - 'columns' => array('slug(255)'), 54 + 'columns' => array('slug'), 55 55 ), 56 56 ), 57 57 ) + parent::getConfiguration();
+1 -1
src/applications/project/storage/PhabricatorProject.php
··· 122 122 'subprojectPHIDs' => self::SERIALIZATION_JSON, 123 123 ), 124 124 self::CONFIG_COLUMN_SCHEMA => array( 125 - 'name' => 'text255', 125 + 'name' => 'text128', 126 126 'status' => 'text32', 127 127 'phrictionSlug' => 'text128?', 128 128 'isMembershipLocked' => 'bool',
+1 -1
src/applications/releeph/storage/ReleephProject.php
··· 30 30 'details' => self::SERIALIZATION_JSON, 31 31 ), 32 32 self::CONFIG_COLUMN_SCHEMA => array( 33 - 'name' => 'text255', 33 + 'name' => 'text128', 34 34 'trunkBranch' => 'text255', 35 35 'isActive' => 'bool', 36 36 ),
+1 -1
src/applications/repository/storage/PhabricatorRepository.php
··· 97 97 'unique' => true, 98 98 ), 99 99 'key_name' => array( 100 - 'columns' => array('name'), 100 + 'columns' => array('name(128)'), 101 101 ), 102 102 'key_vcs' => array( 103 103 'columns' => array('versionControlSystem'),
+1 -1
src/applications/repository/storage/PhabricatorRepositoryArcanistProject.php
··· 23 23 'symbolIndexProjects' => self::SERIALIZATION_JSON, 24 24 ), 25 25 self::CONFIG_COLUMN_SCHEMA => array( 26 - 'name' => 'text255', 26 + 'name' => 'text128', 27 27 'repositoryID' => 'id?', 28 28 ), 29 29 self::CONFIG_KEY_SCHEMA => array(
+1 -1
src/applications/repository/storage/PhabricatorRepositoryBranch.php
··· 9 9 public function getConfiguration() { 10 10 return array( 11 11 self::CONFIG_COLUMN_SCHEMA => array( 12 - 'name' => 'text255', 12 + 'name' => 'text128', 13 13 'lintCommit' => 'text40?', 14 14 ), 15 15 self::CONFIG_KEY_SCHEMA => array(
+1 -4
src/applications/repository/storage/PhabricatorRepositoryCommitData.php
··· 20 20 'commitDetails' => self::SERIALIZATION_JSON, 21 21 ), 22 22 self::CONFIG_COLUMN_SCHEMA => array( 23 - 'authorName' => 'text255', 23 + 'authorName' => 'text', 24 24 'commitMessage' => 'text', 25 25 ), 26 26 self::CONFIG_KEY_SCHEMA => array( 27 27 'commitID' => array( 28 28 'columns' => array('commitID'), 29 29 'unique' => true, 30 - ), 31 - 'authorName' => array( 32 - 'columns' => array('authorName'), 33 30 ), 34 31 ), 35 32 ) + parent::getConfiguration();
+1 -1
src/applications/repository/storage/PhabricatorRepositorySchemaSpec.php
··· 15 15 id(new PhabricatorRepository())->getApplicationName(), 16 16 PhabricatorRepository::TABLE_BADCOMMIT, 17 17 array( 18 - 'fullCommitName' => 'text255', 18 + 'fullCommitName' => 'text64', 19 19 'description' => 'text', 20 20 ), 21 21 array(
+1 -2
src/applications/search/storage/document/PhabricatorSearchDocumentField.php
··· 22 22 'phid' => array( 23 23 'columns' => array('phid'), 24 24 ), 25 - 26 - // NOTE: This is a fulltext index! Be careful! 27 25 'corpus' => array( 28 26 'columns' => array('corpus'), 27 + 'type' => 'FULLTEXT', 29 28 ), 30 29 ), 31 30 ) + parent::getConfiguration();
+2 -2
src/infrastructure/daemon/workers/storage/PhabricatorWorkerTask.php
··· 17 17 public function getConfiguration() { 18 18 return array( 19 19 self::CONFIG_COLUMN_SCHEMA => array( 20 - 'taskClass' => 'text255', 21 - 'leaseOwner' => 'text255?', 20 + 'taskClass' => 'text64', 21 + 'leaseOwner' => 'text64?', 22 22 'leaseExpires' => 'epoch?', 23 23 'failureCount' => 'uint32', 24 24 'failureTime' => 'epoch?',
+66 -26
src/infrastructure/storage/management/workflow/PhabricatorStorageManagementAdjustWorkflow.php
··· 14 14 } 15 15 16 16 public function execute(PhutilArgumentParser $args) { 17 + $force = $args->getArg('force'); 18 + 17 19 $this->requireAllPatchesApplied(); 18 - $this->adjustSchemata(); 20 + $this->adjustSchemata($force); 19 21 return 0; 20 22 } 21 23 ··· 58 60 return array($comp, $expect, $actual); 59 61 } 60 62 61 - private function adjustSchemata() { 63 + private function adjustSchemata($force) { 62 64 $console = PhutilConsole::getConsole(); 63 65 64 66 $console->writeOut( ··· 98 100 99 101 $table->draw(); 100 102 101 - $console->writeOut( 102 - "\n%s\n", 103 - pht( 104 - "Found %s issues(s) with schemata, detailed above.\n\n". 105 - "You can review issues in more detail from the web interface, ". 106 - "in Config > Database Status.\n\n". 107 - "MySQL needs to copy table data to make some adjustments, so these ". 108 - "migrations may take some time.". 103 + if (!$force) { 104 + $console->writeOut( 105 + "\n%s\n", 106 + pht( 107 + "Found %s issues(s) with schemata, detailed above.\n\n". 108 + "You can review issues in more detail from the web interface, ". 109 + "in Config > Database Status.\n\n". 110 + "MySQL needs to copy table data to make some adjustments, so these ". 111 + "migrations may take some time.". 109 112 110 - // TODO: Remove warning once this stabilizes. 111 - "\n\n". 112 - "WARNING: This workflow is new and unstable. If you continue, you ". 113 - "may unrecoverably destory data. Make sure you have a backup before ". 114 - "you proceed.", 113 + // TODO: Remove warning once this stabilizes. 114 + "\n\n". 115 + "WARNING: This workflow is new and unstable. If you continue, you ". 116 + "may unrecoverably destory data. Make sure you have a backup before ". 117 + "you proceed.", 115 118 116 - new PhutilNumber(count($adjustments)))); 119 + new PhutilNumber(count($adjustments)))); 117 120 118 - $prompt = pht('Fix these schema issues?'); 119 - if (!phutil_console_confirm($prompt, $default_no = true)) { 120 - return; 121 + $prompt = pht('Fix these schema issues?'); 122 + if (!phutil_console_confirm($prompt, $default_no = true)) { 123 + return; 124 + } 121 125 } 122 126 123 127 $console->writeOut( ··· 194 198 break; 195 199 case 'key': 196 200 if (($phase == 0) && $adjust['exists']) { 201 + if ($adjust['name'] == 'PRIMARY') { 202 + $key_name = 'PRIMARY KEY'; 203 + } else { 204 + $key_name = qsprintf($conn, 'KEY %T', $adjust['name']); 205 + } 206 + 197 207 queryfx( 198 208 $conn, 199 - 'ALTER TABLE %T.%T DROP KEY %T', 209 + 'ALTER TABLE %T.%T DROP %Q', 200 210 $adjust['database'], 201 211 $adjust['table'], 202 - $adjust['name']); 212 + $key_name); 203 213 } 204 214 205 215 if (($phase == 2) && $adjust['keep']) { 216 + // Different keys need different creation syntax. Notable 217 + // special cases are primary keys and fulltext keys. 218 + if ($adjust['name'] == 'PRIMARY') { 219 + $key_name = 'PRIMARY KEY'; 220 + } else if ($adjust['indexType'] == 'FULLTEXT') { 221 + $key_name = qsprintf($conn, 'FULLTEXT %T', $adjust['name']); 222 + } else { 223 + if ($adjust['unique']) { 224 + $key_name = qsprintf( 225 + $conn, 226 + 'UNIQUE KEY %T', 227 + $adjust['name']); 228 + } else { 229 + $key_name = qsprintf( 230 + $conn, 231 + '/* NONUNIQUE */ KEY %T', 232 + $adjust['name']); 233 + } 234 + } 235 + 206 236 queryfx( 207 237 $conn, 208 - 'ALTER TABLE %T.%T ADD %Q KEY %T (%Q)', 238 + 'ALTER TABLE %T.%T ADD %Q (%Q)', 209 239 $adjust['database'], 210 240 $adjust['table'], 211 - $adjust['unique'] ? 'UNIQUE' : '/* NONUNIQUE */', 212 - $adjust['name'], 241 + $key_name, 213 242 implode(', ', $adjust['columns'])); 214 243 } 215 244 break; ··· 239 268 foreach ($failed as $failure) { 240 269 list($adjust, $ex) = $failure; 241 270 242 - $pieces = array_select_keys($adjust, array('database', 'table', 'naeme')); 271 + $pieces = array_select_keys($adjust, array('database', 'table', 'name')); 243 272 $pieces = array_filter($pieces); 244 273 $target = implode('.', $pieces); 245 274 ··· 269 298 $issue_missingkey = PhabricatorConfigStorageSchema::ISSUE_MISSINGKEY; 270 299 $issue_columns = PhabricatorConfigStorageSchema::ISSUE_KEYCOLUMNS; 271 300 $issue_unique = PhabricatorConfigStorageSchema::ISSUE_UNIQUE; 272 - 301 + $issue_longkey = PhabricatorConfigStorageSchema::ISSUE_LONGKEY; 273 302 274 303 $adjustments = array(); 275 304 foreach ($comp->getDatabases() as $database_name => $database) { ··· 393 422 $issues[] = $issue_unique; 394 423 } 395 424 425 + // NOTE: We can't really fix this, per se, but we may need to remove 426 + // the key to change the column type. In the best case, the new 427 + // column type won't be overlong and recreating the key really will 428 + // fix the issue. In the worst case, we get the right column type and 429 + // lose the key, which is still better than retaining the key having 430 + // the wrong column type. 431 + if ($key->hasIssue($issue_longkey)) { 432 + $issues[] = $issue_longkey; 433 + } 434 + 396 435 if ($issues) { 397 436 $adjustment = array( 398 437 'kind' => 'key', ··· 408 447 $adjustment += array( 409 448 'columns' => $expect_key->getColumnNames(), 410 449 'unique' => $expect_key->getUnique(), 450 + 'indexType' => $expect_key->getIndexType(), 411 451 ); 412 452 } 413 453
+1 -1
src/infrastructure/storage/schema/PhabricatorStorageSchemaSpec.php
··· 8 8 'meta_data', 9 9 'patch_status', 10 10 array( 11 - 'patch' => 'text255', 11 + 'patch' => 'text128', 12 12 'applied' => 'uint32', 13 13 ), 14 14 array(