@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
1
fork

Configure Feed

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

Support AUTO_INCREMENT in `bin/storage adjust`

Summary:
Ref T1191. When changing the column type of an AUTO_INCREMENT column, we currently may lose the autoincrement attribute.

Instead, support it. This is a bit messy because AUTO_INCREMENT columns interact with PRIMARY KEY columns (tables may only have one AUTO_INCREMENT column, and it must be a primary key). We need to migrate in more phases to avoid this issue.

Introduce new `auto` and `auto64` types to represent autoincrement IDs.

Test Plan:
- Saw autoincrement show up correctly in web UI.
- Fixed an autoincrement issue on the XHProf storage table with `bin/storage adjust` safely.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T1191

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

+182 -66
+1 -1
src/applications/cache/storage/PhabricatorCacheSchemaSpec.php
··· 9 9 'cache', 10 10 id(new PhabricatorKeyValueDatabaseCache())->getTableName(), 11 11 array( 12 - 'id' => 'id64', 12 + 'id' => 'auto64', 13 13 'cacheKeyHash' => 'bytes12', 14 14 'cacheKey' => 'text128', 15 15 'cacheFormat' => 'text16',
+1 -1
src/applications/conduit/storage/PhabricatorConduitMethodCallLog.php
··· 13 13 public function getConfiguration() { 14 14 return array( 15 15 self::CONFIG_COLUMN_SCHEMA => array( 16 - 'id' => 'id64', 16 + 'id' => 'auto64', 17 17 'connectionID' => 'id64?', 18 18 'method' => 'text64', 19 19 'error' => 'text255',
+18
src/applications/config/controller/PhabricatorConfigDatabaseStatusController.php
··· 282 282 $unique_issue = PhabricatorConfigStorageSchema::ISSUE_UNIQUE; 283 283 $columns_issue = PhabricatorConfigStorageSchema::ISSUE_KEYCOLUMNS; 284 284 $longkey_issue = PhabricatorConfigStorageSchema::ISSUE_LONGKEY; 285 + $auto_issue = PhabricatorConfigStorageSchema::ISSUE_AUTOINCREMENT; 285 286 286 287 $database = $comp->getDatabase($database_name); 287 288 if (!$database) { ··· 340 341 $this->renderBoolean($column->getNullable()), 341 342 $column->hasIssue($nullable_issue)), 342 343 $this->renderAttr( 344 + $this->renderBoolean($column->getAutoIncrement()), 345 + $column->hasIssue($auto_issue)), 346 + $this->renderAttr( 343 347 $column->getCharacterSet(), 344 348 $column->hasIssue($charset_issue)), 345 349 $this->renderAttr( ··· 356 360 pht('Data Type'), 357 361 pht('Column Type'), 358 362 pht('Nullable'), 363 + pht('Autoincrement'), 359 364 pht('Character Set'), 360 365 pht('Collation'), 361 366 )) ··· 363 368 array( 364 369 null, 365 370 'wide pri', 371 + null, 366 372 null, 367 373 null, 368 374 null, ··· 521 527 $actual_charset = $actual_column->getCharacterSet(); 522 528 $actual_collation = $actual_column->getCollation(); 523 529 $actual_nullable = $actual_column->getNullable(); 530 + $actual_auto = $actual_column->getAutoIncrement(); 524 531 } else { 525 532 $actual_coltype = null; 526 533 $actual_charset = null; 527 534 $actual_collation = null; 528 535 $actual_nullable = null; 536 + $actual_auto = null; 529 537 } 530 538 531 539 if ($expect_column) { ··· 534 542 $expect_charset = $expect_column->getCharacterSet(); 535 543 $expect_collation = $expect_column->getCollation(); 536 544 $expect_nullable = $expect_column->getNullable(); 545 + $expect_auto = $expect_column->getAutoIncrement(); 537 546 } else { 538 547 $data_type = null; 539 548 $expect_coltype = null; 540 549 $expect_charset = null; 541 550 $expect_collation = null; 542 551 $expect_nullable = null; 552 + $expect_auto = null; 543 553 } 544 554 545 555 ··· 586 596 array( 587 597 pht('Expected Nullable'), 588 598 $this->renderBoolean($expect_nullable), 599 + ), 600 + array( 601 + pht('Autoincrement'), 602 + $this->renderBoolean($actual_auto), 603 + ), 604 + array( 605 + pht('Expected Autoincrement'), 606 + $this->renderBoolean($expect_auto), 589 607 ), 590 608 ), 591 609 $column->getIssues());
+14
src/applications/config/schema/PhabricatorConfigColumnSchema.php
··· 8 8 private $columnType; 9 9 private $dataType; 10 10 private $nullable; 11 + private $autoIncrement; 12 + 13 + public function setAutoIncrement($auto_increment) { 14 + $this->autoIncrement = $auto_increment; 15 + return $this; 16 + } 17 + 18 + public function getAutoIncrement() { 19 + return $this->autoIncrement; 20 + } 11 21 12 22 public function setNullable($nullable) { 13 23 $this->nullable = $nullable; ··· 129 139 130 140 if ($this->getNullable() !== $expect->getNullable()) { 131 141 $issues[] = self::ISSUE_NULLABLE; 142 + } 143 + 144 + if ($this->getAutoIncrement() !== $expect->getAutoIncrement()) { 145 + $issues[] = self::ISSUE_AUTOINCREMENT; 132 146 } 133 147 134 148 return $issues;
+9 -2
src/applications/config/schema/PhabricatorConfigSchemaQuery.php
··· 60 60 $column_info = queryfx_all( 61 61 $conn, 62 62 'SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME, 63 - COLLATION_NAME, COLUMN_TYPE, IS_NULLABLE 63 + COLLATION_NAME, COLUMN_TYPE, IS_NULLABLE, EXTRA 64 64 FROM INFORMATION_SCHEMA.COLUMNS 65 65 WHERE (%Q)', 66 66 '('.implode(') OR (', $sql).')'); ··· 96 96 97 97 $columns = idx($database_column_info, $table_name, array()); 98 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 + 99 105 $column_schema = id(new PhabricatorConfigColumnSchema()) 100 106 ->setName($column['COLUMN_NAME']) 101 107 ->setCharacterSet($column['CHARACTER_SET_NAME']) 102 108 ->setCollation($column['COLLATION_NAME']) 103 109 ->setColumnType($column['COLUMN_TYPE']) 104 - ->setNullable($column['IS_NULLABLE'] == 'YES'); 110 + ->setNullable($column['IS_NULLABLE'] == 'YES') 111 + ->setAutoIncrement($auto_increment); 105 112 106 113 $table_schema->addColumn($column_schema); 107 114 }
+14 -4
src/applications/config/schema/PhabricatorConfigSchemaSpec.php
··· 102 102 } 103 103 104 104 $details = $this->getDetailsForDataType($type); 105 - list($column_type, $charset, $collation, $nullable) = $details; 105 + list($column_type, $charset, $collation, $nullable, $auto) = $details; 106 106 107 107 $column = $this->newColumn($name) 108 108 ->setDataType($type) 109 109 ->setColumnType($column_type) 110 110 ->setCharacterSet($charset) 111 111 ->setCollation($collation) 112 - ->setNullable($nullable); 112 + ->setNullable($nullable) 113 + ->setAutoIncrement($auto); 113 114 114 115 $table->addColumn($column); 115 116 } ··· 162 163 $object->getApplicationName(), 163 164 PhabricatorEdgeConfig::TABLE_NAME_EDGEDATA, 164 165 array( 165 - 'id' => 'id', 166 + 'id' => 'auto', 166 167 'data' => 'text', 167 168 ), 168 169 array( ··· 233 234 $column_type = null; 234 235 $charset = null; 235 236 $collation = null; 237 + $auto = false; 236 238 237 239 // If the type ends with "?", make the column nullable. 238 240 $nullable = false; ··· 246 248 // totally disallowed in a MODIFY statement vs a CREATE TABLE statement. 247 249 248 250 switch ($data_type) { 251 + case 'auto': 252 + $column_type = 'int(10) unsigned'; 253 + $auto = true; 254 + break; 255 + case 'auto64': 256 + $column_type = 'bigint(20) unsigned'; 257 + $auto = true; 258 + break; 249 259 case 'id': 250 260 case 'epoch': 251 261 case 'uint32': ··· 392 402 break; 393 403 } 394 404 395 - return array($column_type, $charset, $collation, $nullable); 405 + return array($column_type, $charset, $collation, $nullable, $auto); 396 406 } 397 407 398 408 }
+6
src/applications/config/schema/PhabricatorConfigStorageSchema.php
··· 15 15 const ISSUE_LONGKEY = 'longkey'; 16 16 const ISSUE_SUBWARN = 'subwarn'; 17 17 const ISSUE_SUBFAIL = 'subfail'; 18 + const ISSUE_AUTOINCREMENT = 'autoincrement'; 18 19 19 20 const STATUS_OKAY = 'okay'; 20 21 const STATUS_WARN = 'warn'; ··· 124 125 return pht('Subschemata Have Warnings'); 125 126 case self::ISSUE_SUBFAIL: 126 127 return pht('Subschemata Have Failures'); 128 + case self::ISSUE_AUTOINCREMENT: 129 + return pht('Column has Wrong Autoincrement'); 127 130 default: 128 131 throw new Exception(pht('Unknown schema issue "%s"!', $issue)); 129 132 } ··· 157 160 return pht('Subschemata have setup warnings.'); 158 161 case self::ISSUE_SUBFAIL: 159 162 return pht('Subschemata have setup failures.'); 163 + case self::ISSUE_AUTOINCREMENT: 164 + return pht('This column has the wrong autoincrement setting.'); 160 165 default: 161 166 throw new Exception(pht('Unknown schema issue "%s"!', $issue)); 162 167 } ··· 178 183 case self::ISSUE_UNIQUE: 179 184 case self::ISSUE_KEYCOLUMNS: 180 185 case self::ISSUE_LONGKEY: 186 + case self::ISSUE_AUTOINCREMENT: 181 187 return self::STATUS_WARN; 182 188 default: 183 189 throw new Exception(pht('Unknown schema issue "%s"!', $issue));
+1 -1
src/applications/fact/storage/PhabricatorFactAggregate.php
··· 9 9 public function getConfiguration() { 10 10 return array( 11 11 self::CONFIG_COLUMN_SCHEMA => array( 12 - 'id' => 'id64', 12 + 'id' => 'auto64', 13 13 'factType' => 'text32', 14 14 'valueX' => 'uint64', 15 15 ),
+1 -1
src/applications/fact/storage/PhabricatorFactRaw.php
··· 15 15 public function getConfiguration() { 16 16 return array( 17 17 self::CONFIG_COLUMN_SCHEMA => array( 18 - 'id' => 'id64', 18 + 'id' => 'auto64', 19 19 'factType' => 'text32', 20 20 'objectA' => 'phid', 21 21 'valueX' => 'sint64',
+1 -1
src/applications/harbormaster/storage/HarbormasterSchemaSpec.php
··· 24 24 id(new HarbormasterBuildable())->getApplicationName(), 25 25 'harbormaster_buildlogchunk', 26 26 array( 27 - 'id' => 'id', 27 + 'id' => 'auto', 28 28 'logID' => 'id', 29 29 'encoding' => 'text32', 30 30
+1 -1
src/applications/project/storage/PhabricatorProjectSchemaSpec.php
··· 24 24 id(new PhabricatorProject())->getApplicationName(), 25 25 PhabricatorProject::TABLE_DATASOURCE_TOKEN, 26 26 array( 27 - 'id' => 'id', 27 + 'id' => 'auto', 28 28 'projectID' => 'id', 29 29 'token' => 'text128', 30 30 ),
+4 -4
src/applications/repository/storage/PhabricatorRepositorySchemaSpec.php
··· 29 29 id(new PhabricatorRepository())->getApplicationName(), 30 30 PhabricatorRepository::TABLE_COVERAGE, 31 31 array( 32 - 'id' => 'id', 32 + 'id' => 'auto', 33 33 'branchID' => 'id', 34 34 'commitID' => 'id', 35 35 'pathID' => 'id', ··· 70 70 id(new PhabricatorRepository())->getApplicationName(), 71 71 PhabricatorRepository::TABLE_LINTMESSAGE, 72 72 array( 73 - 'id' => 'id', 73 + 'id' => 'auto', 74 74 'branchID' => 'id', 75 75 'path' => 'text', 76 76 'line' => 'uint32', ··· 100 100 id(new PhabricatorRepository())->getApplicationName(), 101 101 PhabricatorRepository::TABLE_PARENTS, 102 102 array( 103 - 'id' => 'id', 103 + 'id' => 'auto', 104 104 'childCommitID' => 'id', 105 105 'parentCommitID' => 'id', 106 106 ), ··· 122 122 id(new PhabricatorRepository())->getApplicationName(), 123 123 PhabricatorRepository::TABLE_PATH, 124 124 array( 125 - 'id' => 'id', 125 + 'id' => 'auto', 126 126 'path' => 'text', 127 127 'pathHash' => 'bytes32', 128 128 ),
+1
src/applications/tokens/storage/PhabricatorTokenCount.php
··· 10 10 self::CONFIG_IDS => self::IDS_MANUAL, 11 11 self::CONFIG_TIMESTAMPS => false, 12 12 self::CONFIG_COLUMN_SCHEMA => array( 13 + 'id' => 'auto', 13 14 'tokenCount' => 'uint32', 14 15 ), 15 16 self::CONFIG_KEY_SCHEMA => array(
+6 -1
src/infrastructure/daemon/workers/storage/PhabricatorWorkerArchiveTask.php
··· 10 10 protected $result; 11 11 12 12 public function getConfiguration() { 13 - $config = parent::getConfiguration(); 13 + $config = array( 14 + // We manage the IDs in this table; they are allocated in the ActiveTask 15 + // table and moved here without alteration. 16 + self::CONFIG_IDS => self::IDS_MANUAL, 17 + ) + parent::getConfiguration(); 18 + 14 19 15 20 $config[self::CONFIG_COLUMN_SCHEMA] = array( 16 21 'result' => 'uint32',
+8 -1
src/infrastructure/storage/lisk/LiskDAO.php
··· 1744 1744 1745 1745 $binary_map = $this->getBinaryColumns(); 1746 1746 1747 + $id_mechanism = $this->getConfigOption(self::CONFIG_IDS); 1748 + if ($id_mechanism == self::IDS_AUTOINCREMENT) { 1749 + $id_type = 'auto'; 1750 + } else { 1751 + $id_type = 'id'; 1752 + } 1753 + 1747 1754 $builtin = array( 1748 - 'id' => 'id', 1755 + 'id' => $id_type, 1749 1756 'phid' => 'phid', 1750 1757 'viewPolicy' => 'policy', 1751 1758 'editPolicy' => 'policy',
+96 -48
src/infrastructure/storage/management/workflow/PhabricatorStorageManagementAdjustWorkflow.php
··· 132 132 133 133 $failed = array(); 134 134 135 - // We make changes in three phases: 136 - // 137 - // Phase 0: Drop all keys which we're going to adjust. This prevents them 138 - // from interfering with column changes. 139 - // 140 - // Phase 1: Apply all database, table, and column changes. 141 - // 142 - // Phase 2: Restore adjusted keys. 143 - $phases = 3; 135 + // We make changes in several phases. 136 + $phases = array( 137 + // Drop surplus autoincrements. This allows us to drop primary keys on 138 + // autoincrement columns. 139 + 'drop_auto', 140 + 141 + // Drop all keys we're going to adjust. This prevents them from 142 + // interfering with column changes. 143 + 'drop_keys', 144 + 145 + // Apply all database, table, and column changes. 146 + 'main', 147 + 148 + // Restore adjusted keys. 149 + 'add_keys', 150 + 151 + // Add missing autoincrements. 152 + 'add_auto', 153 + ); 144 154 145 155 $bar = id(new PhutilConsoleProgressBar()) 146 - ->setTotal(count($adjustments) * $phases); 156 + ->setTotal(count($adjustments) * count($phases)); 147 157 148 - for ($phase = 0; $phase < $phases; $phase++) { 158 + foreach ($phases as $phase) { 149 159 foreach ($adjustments as $adjust) { 150 160 try { 151 161 switch ($adjust['kind']) { 152 162 case 'database': 153 - if ($phase != 1) { 154 - break; 163 + if ($phase == 'main') { 164 + queryfx( 165 + $conn, 166 + 'ALTER DATABASE %T CHARACTER SET = %s COLLATE = %s', 167 + $adjust['database'], 168 + $adjust['charset'], 169 + $adjust['collation']); 155 170 } 156 - queryfx( 157 - $conn, 158 - 'ALTER DATABASE %T CHARACTER SET = %s COLLATE = %s', 159 - $adjust['database'], 160 - $adjust['charset'], 161 - $adjust['collation']); 162 171 break; 163 172 case 'table': 164 - if ($phase != 1) { 165 - break; 173 + if ($phase == 'main') { 174 + queryfx( 175 + $conn, 176 + 'ALTER TABLE %T.%T COLLATE = %s', 177 + $adjust['database'], 178 + $adjust['table'], 179 + $adjust['collation']); 166 180 } 167 - queryfx( 168 - $conn, 169 - 'ALTER TABLE %T.%T COLLATE = %s', 170 - $adjust['database'], 171 - $adjust['table'], 172 - $adjust['collation']); 173 181 break; 174 182 case 'column': 175 - if ($phase != 1) { 176 - break; 177 - } 178 - $parts = array(); 179 - if ($adjust['charset']) { 180 - $parts[] = qsprintf( 181 - $conn, 182 - 'CHARACTER SET %Q COLLATE %Q', 183 - $adjust['charset'], 184 - $adjust['collation']); 183 + $apply = false; 184 + $auto = false; 185 + $new_auto = idx($adjust, 'auto'); 186 + if ($phase == 'drop_auto') { 187 + if ($new_auto === false) { 188 + $apply = true; 189 + $auto = false; 190 + } 191 + } else if ($phase == 'main') { 192 + $apply = true; 193 + if ($new_auto === false) { 194 + $auto = false; 195 + } else { 196 + $auto = $adjust['is_auto']; 197 + } 198 + } else if ($phase == 'add_auto') { 199 + if ($new_auto === true) { 200 + $apply = true; 201 + $auto = true; 202 + } 185 203 } 186 204 187 - queryfx( 188 - $conn, 189 - 'ALTER TABLE %T.%T MODIFY %T %Q %Q %Q', 190 - $adjust['database'], 191 - $adjust['table'], 192 - $adjust['name'], 193 - $adjust['type'], 194 - implode(' ', $parts), 195 - $adjust['nullable'] ? 'NULL' : 'NOT NULL'); 205 + if ($apply) { 206 + $parts = array(); 196 207 208 + if ($auto) { 209 + $parts[] = qsprintf( 210 + $conn, 211 + 'AUTO_INCREMENT'); 212 + } 213 + 214 + if ($adjust['charset']) { 215 + $parts[] = qsprintf( 216 + $conn, 217 + 'CHARACTER SET %Q COLLATE %Q', 218 + $adjust['charset'], 219 + $adjust['collation']); 220 + } 221 + 222 + queryfx( 223 + $conn, 224 + 'ALTER TABLE %T.%T MODIFY %T %Q %Q %Q', 225 + $adjust['database'], 226 + $adjust['table'], 227 + $adjust['name'], 228 + $adjust['type'], 229 + implode(' ', $parts), 230 + $adjust['nullable'] ? 'NULL' : 'NOT NULL'); 231 + } 197 232 break; 198 233 case 'key': 199 234 if (($phase == 0) && $adjust['exists']) { ··· 298 333 $issue_columns = PhabricatorConfigStorageSchema::ISSUE_KEYCOLUMNS; 299 334 $issue_unique = PhabricatorConfigStorageSchema::ISSUE_UNIQUE; 300 335 $issue_longkey = PhabricatorConfigStorageSchema::ISSUE_LONGKEY; 336 + $issue_auto = PhabricatorConfigStorageSchema::ISSUE_AUTOINCREMENT; 301 337 302 338 $adjustments = array(); 303 339 foreach ($comp->getDatabases() as $database_name => $database) { ··· 368 404 if ($column->hasIssue($issue_columntype)) { 369 405 $issues[] = $issue_columntype; 370 406 } 407 + if ($column->hasIssue($issue_auto)) { 408 + $issues[] = $issue_auto; 409 + } 371 410 372 411 if ($issues) { 373 412 if ($expect_column->getCharacterSet() === null) { ··· 380 419 $collation = $expect_column->getCollation(); 381 420 } 382 421 383 - 384 - $adjustments[] = array( 422 + $adjustment = array( 385 423 'kind' => 'column', 386 424 'database' => $database_name, 387 425 'table' => $table_name, ··· 394 432 // NOTE: We don't adjust column nullability because it is 395 433 // dangerous, so always use the current nullability. 396 434 'nullable' => $actual_column->getNullable(), 435 + 436 + // NOTE: This always stores the current value, because we have 437 + // to make these updates separately. 438 + 'is_auto' => $actual_column->getAutoIncrement(), 397 439 ); 440 + 441 + if ($column->hasIssue($issue_auto)) { 442 + $adjustment['auto'] = $expect_column->getAutoIncrement(); 443 + } 444 + 445 + $adjustments[] = $adjustment; 398 446 } 399 447 } 400 448