@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.

Allow `bin/storage adjust` to correct column types and collations

Summary:
Ref T1191. Allow `bin/storage adjust` to modify columns.

- Although `CREATE TABLE ... colname VARCHAR(64) CHARACTER SET BINARY` works fine, it's actually a trick. Adjust the binary columns for this.

Test Plan: See comment.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T6130, T6128, T6135, T6137, T6138, T6149, T6151, T1191

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

+138 -44
+11 -21
src/applications/config/schema/PhabricatorConfigSchemaSpec.php
··· 230 230 $data_type = substr($data_type, 0, -1); 231 231 } 232 232 233 + // NOTE: MySQL allows fragments like "VARCHAR(32) CHARACTER SET binary", 234 + // but just interprets that to mean "VARBINARY(32)". The fragment is 235 + // totally disallowed in a MODIFY statement vs a CREATE TABLE statement. 236 + 233 237 switch ($data_type) { 234 238 case 'id': 235 239 case 'epoch': ··· 248 252 break; 249 253 case 'phid': 250 254 case 'policy'; 251 - $column_type = 'varchar(64)'; 252 - $charset = 'binary'; 253 - $collation = 'binary'; 255 + $column_type = 'varbinary(64)'; 254 256 break; 255 257 case 'bytes64': 256 - $column_type = 'char(64)'; 257 - $charset = 'binary'; 258 - $collation = 'binary'; 258 + $column_type = 'binary(64)'; 259 259 break; 260 260 case 'bytes40': 261 - $column_type = 'char(40)'; 262 - $charset = 'binary'; 263 - $collation = 'binary'; 261 + $column_type = 'binary(40)'; 264 262 break; 265 263 case 'bytes32': 266 - $column_type = 'char(32)'; 267 - $charset = 'binary'; 268 - $collation = 'binary'; 264 + $column_type = 'binary(32)'; 269 265 break; 270 266 case 'bytes20': 271 - $column_type = 'char(20)'; 272 - $charset = 'binary'; 273 - $collation = 'binary'; 267 + $column_type = 'binary(20)'; 274 268 break; 275 269 case 'bytes12': 276 - $column_type = 'char(12)'; 277 - $charset = 'binary'; 278 - $collation = 'binary'; 270 + $column_type = 'binary(12)'; 279 271 break; 280 272 case 'bytes4': 281 - $column_type = 'char(4)'; 282 - $charset = 'binary'; 283 - $collation = 'binary'; 273 + $column_type = 'binary(4)'; 284 274 break; 285 275 case 'bytes': 286 276 $column_type = 'longblob';
+127 -23
src/infrastructure/storage/management/workflow/PhabricatorStorageManagementAdjustWorkflow.php
··· 127 127 $api = $this->getAPI(); 128 128 $conn = $api->getConn(null); 129 129 130 + $failed = array(); 131 + 130 132 $bar = id(new PhutilConsoleProgressBar()) 131 133 ->setTotal(count($adjustments)); 132 134 foreach ($adjustments as $adjust) { 133 - switch ($adjust['kind']) { 134 - case 'database': 135 - queryfx( 136 - $conn, 137 - 'ALTER DATABASE %T CHARACTER SET = %s COLLATE = %s', 138 - $adjust['database'], 139 - $adjust['charset'], 140 - $adjust['collation']); 141 - break; 142 - case 'table': 143 - queryfx( 144 - $conn, 145 - 'ALTER TABLE %T.%T COLLATE = %s', 146 - $adjust['database'], 147 - $adjust['table'], 148 - $adjust['collation']); 149 - break; 150 - default: 151 - throw new Exception( 152 - pht('Unknown schema adjustment kind "%s"!', $adjust['kind'])); 135 + try { 136 + switch ($adjust['kind']) { 137 + case 'database': 138 + queryfx( 139 + $conn, 140 + 'ALTER DATABASE %T CHARACTER SET = %s COLLATE = %s', 141 + $adjust['database'], 142 + $adjust['charset'], 143 + $adjust['collation']); 144 + break; 145 + case 'table': 146 + queryfx( 147 + $conn, 148 + 'ALTER TABLE %T.%T COLLATE = %s', 149 + $adjust['database'], 150 + $adjust['table'], 151 + $adjust['collation']); 152 + break; 153 + case 'column': 154 + $parts = array(); 155 + if ($adjust['charset']) { 156 + $parts[] = qsprintf( 157 + $conn, 158 + 'CHARACTER SET %Q COLLATE %Q', 159 + $adjust['charset'], 160 + $adjust['collation']); 161 + } 162 + 163 + queryfx( 164 + $conn, 165 + 'ALTER TABLE %T.%T MODIFY %T %Q %Q %Q', 166 + $adjust['database'], 167 + $adjust['table'], 168 + $adjust['name'], 169 + $adjust['type'], 170 + implode(' ', $parts), 171 + $adjust['nullable'] ? 'NULL' : 'NOT NULL'); 172 + 173 + break; 174 + default: 175 + throw new Exception( 176 + pht('Unknown schema adjustment kind "%s"!', $adjust['kind'])); 177 + } 178 + } catch (AphrontQueryException $ex) { 179 + $failed[] = array($adjust, $ex); 153 180 } 154 - 155 181 $bar->update(1); 156 182 } 157 183 $bar->done(); 158 184 185 + if (!$failed) { 186 + $console->writeOut( 187 + "%s\n", 188 + pht('Completed fixing all schema issues.')); 189 + return; 190 + } 191 + 192 + $table = id(new PhutilConsoleTable()) 193 + ->addColumn('target', array('title' => pht('Target'))) 194 + ->addColumn('error', array('title' => pht('Error'))); 195 + 196 + foreach ($failed as $failure) { 197 + list($adjust, $ex) = $failure; 198 + 199 + $pieces = array_select_keys($adjust, array('database', 'table', 'naeme')); 200 + $pieces = array_filter($pieces); 201 + $target = implode('.', $pieces); 202 + 203 + $table->addRow( 204 + array( 205 + 'target' => $target, 206 + 'error' => $ex->getMessage(), 207 + )); 208 + } 209 + 210 + $console->writeOut("\n"); 211 + $table->draw(); 159 212 $console->writeOut( 160 - "%s\n", 161 - pht('Completed fixing all schema issues.')); 213 + "\n%s\n", 214 + pht('Failed to make some schema adjustments, detailed above.')); 215 + 216 + return 1; 162 217 } 163 218 164 219 private function findAdjustments() { ··· 166 221 167 222 $issue_charset = PhabricatorConfigStorageSchema::ISSUE_CHARSET; 168 223 $issue_collation = PhabricatorConfigStorageSchema::ISSUE_COLLATION; 224 + $issue_columntype = PhabricatorConfigStorageSchema::ISSUE_COLUMNTYPE; 169 225 170 226 $adjustments = array(); 171 227 foreach ($comp->getDatabases() as $database_name => $database) { ··· 216 272 'issues' => $issues, 217 273 'collation' => $expect_table->getCollation(), 218 274 ); 275 + } 276 + 277 + foreach ($table->getColumns() as $column_name => $column) { 278 + $expect_column = $expect_table->getColumn($column_name); 279 + $actual_column = $actual_table->getColumn($column_name); 280 + 281 + if (!$expect_column || !$actual_column) { 282 + continue; 283 + } 284 + 285 + $issues = array(); 286 + if ($column->hasIssue($issue_collation)) { 287 + $issues[] = $issue_collation; 288 + } 289 + if ($column->hasIssue($issue_charset)) { 290 + $issues[] = $issue_charset; 291 + } 292 + if ($column->hasIssue($issue_columntype)) { 293 + $issues[] = $issue_columntype; 294 + } 295 + 296 + if ($issues) { 297 + if ($expect_column->getCharacterSet() === null) { 298 + // For non-text columns, we won't be specifying a collation or 299 + // character set. 300 + $charset = null; 301 + $collation = null; 302 + } else { 303 + $charset = $expect_column->getCharacterSet(); 304 + $collation = $expect_column->getCollation(); 305 + } 306 + 307 + 308 + $adjustments[] = array( 309 + 'kind' => 'column', 310 + 'database' => $database_name, 311 + 'table' => $table_name, 312 + 'name' => $column_name, 313 + 'issues' => $issues, 314 + 'collation' => $collation, 315 + 'charset' => $charset, 316 + 'type' => $expect_column->getColumnType(), 317 + 318 + // NOTE: We don't adjust column nullability because it is 319 + // dangerous, so always use the current nullability. 320 + 'nullable' => $actual_column->getNullable(), 321 + ); 322 + } 219 323 } 220 324 } 221 325 }