Skip to content

Commit 05f0596

Browse files
authored
Merge pull request #7 from orisintel/feature/trim-underscores-from-foreign
feat(MigrateDumpCommand): Trim underscores from foreign-key constrain…
2 parents 73282e0 + df21d44 commit 05f0596

File tree

3 files changed

+89
-0
lines changed

3 files changed

+89
-0
lines changed

config/migration-snapshot.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,18 @@
3131
*/
3232

3333
'reorder' => env('MIGRATION_SNAPSHOT_REORDER', false),
34+
35+
/*
36+
|--------------------------------------------------------------------------
37+
| Whether to trim underscores from foreign constraints for consistency.
38+
|--------------------------------------------------------------------------
39+
|
40+
| Percona's Online Schema Change for Mysql may prepend foreign constraints
41+
| with underscores. Since it may not be used in all environments some dumped
42+
| snapshots may not match, adding unnecessary noise to source control.
43+
| Enable this trimming to get more consistent snapshots when PTOSC may be
44+
| used.
45+
|
46+
*/
47+
'trim-underscores' => env('MIGRATION_SNAPSHOT_TRIM_UNDERSCORES', false),
3448
];

src/Commands/MigrateDumpCommand.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ private static function mysqlDump(array $db_config, string $schema_sql_path) : i
129129
return 1;
130130
}
131131
$schema_sql = preg_replace('/\s+AUTO_INCREMENT=[0-9]+/iu', '', $schema_sql);
132+
$schema_sql = self::trimUnderscoresFromForeign($schema_sql);
132133
if (false === file_put_contents($schema_sql_path, $schema_sql)) {
133134
return 1;
134135
}
@@ -156,6 +157,55 @@ private static function mysqlDump(array $db_config, string $schema_sql_path) : i
156157
return $exit_code;
157158
}
158159

160+
/**
161+
* Trim underscores from FK constraint names to workaround PTOSC quirk.
162+
*
163+
* @param string $sql like "CONSTRAINT _my_fk FOREIGN KEY ..."
164+
*
165+
* @return string without leading underscores like "CONSTRAINT my_fk ...".
166+
*/
167+
public static function trimUnderscoresFromForeign(string $sql) : string
168+
{
169+
if (! config('migration-snapshot.trim-underscores')) {
170+
return $sql;
171+
}
172+
173+
$trimmed = preg_replace(
174+
'/(^|,)(\s*CONSTRAINT\s+[`"]?)_+(.*?[`"]?\s+FOREIGN\s+KEY\b.*)/imu',
175+
'\1\2\3',
176+
$sql
177+
);
178+
179+
// Reorder constraints for consistency since dump put underscored first.
180+
$offset = 0;
181+
// Sort each adjacent block of constraints.
182+
while (preg_match('/((?:^|,)?\s*CONSTRAINT\s+.*?(?:,|\)\s*\)))+/imu', $trimmed, $m, PREG_OFFSET_CAPTURE, $offset)) {
183+
// Bump offset to avoid unintentionally reprocessing already sorted.
184+
$offset = $m[count($m) - 1][1] + strlen($m[count($m) - 1][0]);
185+
$constraints_original = $m[0][0];
186+
if (! preg_match_all('/(?:^|,)\s*CONSTRAINT\s+.*?(?:,|\)\s*\))/imu', $constraints_original, $m)) {
187+
continue;
188+
}
189+
$constraints_array = $m[0];
190+
foreach ($constraints_array as &$constraint) {
191+
$constraint = trim($constraint, ",\r\n");
192+
// Trim extra parenthesis at the end of table definitions.
193+
$constraint = preg_replace('/(\s*\))\s*\)\z/imu', '\1', $constraint, 1);
194+
}
195+
sort($constraints_array);
196+
$separator = ',' . PHP_EOL;
197+
// Comma or "\n)".
198+
$terminator = preg_match('/(,|\s*\))\z/imu', $constraints_original, $m)
199+
? $m[1] : '';
200+
$constraints_sorted = $separator
201+
. implode($separator, $constraints_array)
202+
. $terminator;
203+
$trimmed = str_replace($constraints_original, $constraints_sorted, $trimmed);
204+
}
205+
206+
return $trimmed;
207+
}
208+
159209
/**
160210
* @param array $db_config like ['host' => , 'port' => ].
161211
*

tests/Mysql/MigrateDumpTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class MigrateDumpTest extends TestCase
1111
protected function getEnvironmentSetUp($app)
1212
{
1313
$app['config']->set('migration-snapshot.reorder', true);
14+
$app['config']->set('migration-snapshot.trim-underscores', true);
1415
}
1516

1617
public function test_handle()
@@ -28,6 +29,30 @@ public function test_handle()
2829
$this->assertRegExp("/[\r\n]\z/mu", $last_character);
2930
}
3031

32+
public function test_trimUnderscoresFromForeign()
33+
{
34+
$sql = "KEY z_index,
35+
CONSTRAINT `__b_fk` FOREIGN KEY (`b`) REFERENCES `b` ON(`b`),
36+
CONSTRAINT `a_fk` FOREIGN KEY (`a`) REFERENCES `a` ON(`a`)
37+
);
38+
...KEY z2_index,
39+
CONSTRAINT `__d_fk` FOREIGN KEY (`d`) REFERENCES `d` ON(`d`),
40+
CONSTRAINT `c_fk` FOREIGN KEY (`c`) REFERENCES `c` ON(`c`)
41+
);";
42+
$trimmed = MigrateDumpCommand::trimUnderscoresFromForeign($sql);
43+
$this->assertEquals(
44+
"KEY z_index,
45+
CONSTRAINT `a_fk` FOREIGN KEY (`a`) REFERENCES `a` ON(`a`),
46+
CONSTRAINT `b_fk` FOREIGN KEY (`b`) REFERENCES `b` ON(`b`)
47+
);
48+
...KEY z2_index,
49+
CONSTRAINT `c_fk` FOREIGN KEY (`c`) REFERENCES `c` ON(`c`),
50+
CONSTRAINT `d_fk` FOREIGN KEY (`d`) REFERENCES `d` ON(`d`)
51+
);",
52+
$trimmed
53+
);
54+
}
55+
3156
public function test_reorderMigrationRows()
3257
{
3358
$output = [

0 commit comments

Comments
 (0)