From f78cf5bd2c430a8fc5dba3a51b37c80fc06aaa00 Mon Sep 17 00:00:00 2001 From: Paul Rogers Date: Tue, 7 May 2019 11:37:14 -0400 Subject: [PATCH 1/3] feat(MigrateDumpCommand): Trim underscores from foreign-key constraint names and reorder them for consistency to workaround PTOSC quirk. --- config/migration-snapshot.php | 14 +++++++++ src/Commands/MigrateDumpCommand.php | 46 ++++++++++++++++++++++++++++- tests/Mysql/MigrateDumpTest.php | 17 +++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/config/migration-snapshot.php b/config/migration-snapshot.php index 4104d33..349c1d9 100644 --- a/config/migration-snapshot.php +++ b/config/migration-snapshot.php @@ -31,4 +31,18 @@ */ 'reorder' => env('MIGRATION_SNAPSHOT_REORDER', false), + + /* + |-------------------------------------------------------------------------- + | Whether to trim underscores from foreign constraints for consistency. + |-------------------------------------------------------------------------- + | + | Percona's Online Schema Change for Mysql may prepend foreign constraints + | with underscores. Since it may not be used in all environments some dumped + | snapshots may not match, adding unnecessary noise to source control. + | Enable this trimming to get more consistent snapshots when PTOSC may be + | used. + | + */ + 'trim-underscores' => env('MIGRATION_SNAPSHOT_TRIM_UNDERSCORES', false), ]; diff --git a/src/Commands/MigrateDumpCommand.php b/src/Commands/MigrateDumpCommand.php index 7055a1f..0d4cd70 100644 --- a/src/Commands/MigrateDumpCommand.php +++ b/src/Commands/MigrateDumpCommand.php @@ -144,18 +144,62 @@ private static function mysqlDump(array $db_config, string $schema_sql_path) : i } $output = self::reorderMigrationRows($output); + $output_string = implode(PHP_EOL, $output) . PHP_EOL; + $output_string = self::trimUnderscoresFromForeign($output_string); // Append reordered rows, and include a line break to make SCM diffs // easier to read. file_put_contents( $schema_sql_path, - implode(PHP_EOL, $output) . PHP_EOL, + $output_string, FILE_APPEND ); return $exit_code; } + /** + * Trim underscores from FK constraint names to workaround PTOSC quirk. + * + * @param string $sql like "CONSTRAINT _my_fk FOREIGN KEY ..." + * + * @return string without leading underscores like "CONSTRAINT my_fk ...". + */ + public static function trimUnderscoresFromForeign(string $sql) : string + { + if (! config('migration-snapshot.trim-underscores')) { + return $sql; + } + + $trimmed = preg_replace( + '/(^|,)(\s*CONSTRAINT\s+[`"]?)_+(.*?[`"]?\s+FOREIGN\s+KEY\b.*)/imu', + '\1\2\3', + $sql + ); + + // Reorder constraints for consistency since dump put underscored first. + if (preg_match_all('/(?:^|,)\s*CONSTRAINT\s+.*?(?:,|\)\s*\))/imu', $trimmed, $m)) { + $constraints_original = implode(PHP_EOL, $m[0]); + $constraints_array = $m[0]; + foreach ($constraints_array as &$constraint) { + $constraint = trim($constraint, ",\r\n"); + // Trim extra parenthesis at the end of table definitions. + $constraint = preg_replace('/(\s*\))\s*\)\z/imu', '\1', $constraint, 1); + } + sort($constraints_array); + $separator = ',' . PHP_EOL; + // Comma or "\n)". + $terminator = preg_match('/(,|\s*\))\z/imu', $constraints_original, $m) + ? $m[1] : ''; + $constraints_sorted = $separator + . implode($separator, $constraints_array) + . $terminator; + $trimmed = str_replace($constraints_original, $constraints_sorted, $trimmed); + } + + return $trimmed; + } + /** * @param array $db_config like ['host' => , 'port' => ]. * diff --git a/tests/Mysql/MigrateDumpTest.php b/tests/Mysql/MigrateDumpTest.php index 1d9fa14..49e57a7 100644 --- a/tests/Mysql/MigrateDumpTest.php +++ b/tests/Mysql/MigrateDumpTest.php @@ -11,6 +11,7 @@ class MigrateDumpTest extends TestCase protected function getEnvironmentSetUp($app) { $app['config']->set('migration-snapshot.reorder', true); + $app['config']->set('migration-snapshot.trim-underscores', true); } public function test_handle() @@ -28,6 +29,22 @@ public function test_handle() $this->assertRegExp("/[\r\n]\z/mu", $last_character); } + public function test_trimUnderscoresFromForeign() + { + $sql = "KEY z_index, + CONSTRAINT _b_fk FOREIGN KEY('b') REFERENCES b ON('b'), + CONSTRAINT a_fk FOREIGN KEY('a') REFERENCES a ON('a') +);"; + $trimmed = MigrateDumpCommand::trimUnderscoresFromForeign($sql); + $this->assertEquals( + "KEY z_index, + CONSTRAINT a_fk FOREIGN KEY('a') REFERENCES a ON('a'), + CONSTRAINT b_fk FOREIGN KEY('b') REFERENCES b ON('b') +);", + $trimmed + ); + } + public function test_reorderMigrationRows() { $output = [ From e9d5e0ce350007d8ac9772c33aa0f16a8902a228 Mon Sep 17 00:00:00 2001 From: Paul Rogers Date: Tue, 7 May 2019 12:12:34 -0400 Subject: [PATCH 2/3] fix(MigrateDumpCommand): Correct SQL to which is trimmed of underscores. --- src/Commands/MigrateDumpCommand.php | 5 ++--- tests/Mysql/MigrateDumpTest.php | 8 ++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Commands/MigrateDumpCommand.php b/src/Commands/MigrateDumpCommand.php index 0d4cd70..59854bc 100644 --- a/src/Commands/MigrateDumpCommand.php +++ b/src/Commands/MigrateDumpCommand.php @@ -129,6 +129,7 @@ private static function mysqlDump(array $db_config, string $schema_sql_path) : i return 1; } $schema_sql = preg_replace('/\s+AUTO_INCREMENT=[0-9]+/iu', '', $schema_sql); + $schema_sql = self::trimUnderscoresFromForeign($schema_sql); if (false === file_put_contents($schema_sql_path, $schema_sql)) { return 1; } @@ -144,14 +145,12 @@ private static function mysqlDump(array $db_config, string $schema_sql_path) : i } $output = self::reorderMigrationRows($output); - $output_string = implode(PHP_EOL, $output) . PHP_EOL; - $output_string = self::trimUnderscoresFromForeign($output_string); // Append reordered rows, and include a line break to make SCM diffs // easier to read. file_put_contents( $schema_sql_path, - $output_string, + implode(PHP_EOL, $output) . PHP_EOL, FILE_APPEND ); diff --git a/tests/Mysql/MigrateDumpTest.php b/tests/Mysql/MigrateDumpTest.php index 49e57a7..2a82927 100644 --- a/tests/Mysql/MigrateDumpTest.php +++ b/tests/Mysql/MigrateDumpTest.php @@ -32,14 +32,14 @@ public function test_handle() public function test_trimUnderscoresFromForeign() { $sql = "KEY z_index, - CONSTRAINT _b_fk FOREIGN KEY('b') REFERENCES b ON('b'), - CONSTRAINT a_fk FOREIGN KEY('a') REFERENCES a ON('a') + CONSTRAINT `__b_fk` FOREIGN KEY (`b`) REFERENCES `b` ON(`b`), + CONSTRAINT `a_fk` FOREIGN KEY (`a`) REFERENCES `a` ON(`a`) );"; $trimmed = MigrateDumpCommand::trimUnderscoresFromForeign($sql); $this->assertEquals( "KEY z_index, - CONSTRAINT a_fk FOREIGN KEY('a') REFERENCES a ON('a'), - CONSTRAINT b_fk FOREIGN KEY('b') REFERENCES b ON('b') + CONSTRAINT `a_fk` FOREIGN KEY (`a`) REFERENCES `a` ON(`a`), + CONSTRAINT `b_fk` FOREIGN KEY (`b`) REFERENCES `b` ON(`b`) );", $trimmed ); From df21d4465fe1df6682dadca34f025cda5b478a13 Mon Sep 17 00:00:00 2001 From: Paul Rogers Date: Tue, 7 May 2019 12:40:10 -0400 Subject: [PATCH 3/3] fix(MigrateDumpCommand): Correct sorting trimmed constraints when multiple blocks of constraints. --- src/Commands/MigrateDumpCommand.php | 11 +++++++++-- tests/Mysql/MigrateDumpTest.php | 8 ++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Commands/MigrateDumpCommand.php b/src/Commands/MigrateDumpCommand.php index 59854bc..2c85537 100644 --- a/src/Commands/MigrateDumpCommand.php +++ b/src/Commands/MigrateDumpCommand.php @@ -177,8 +177,15 @@ public static function trimUnderscoresFromForeign(string $sql) : string ); // Reorder constraints for consistency since dump put underscored first. - if (preg_match_all('/(?:^|,)\s*CONSTRAINT\s+.*?(?:,|\)\s*\))/imu', $trimmed, $m)) { - $constraints_original = implode(PHP_EOL, $m[0]); + $offset = 0; + // Sort each adjacent block of constraints. + while (preg_match('/((?:^|,)?\s*CONSTRAINT\s+.*?(?:,|\)\s*\)))+/imu', $trimmed, $m, PREG_OFFSET_CAPTURE, $offset)) { + // Bump offset to avoid unintentionally reprocessing already sorted. + $offset = $m[count($m) - 1][1] + strlen($m[count($m) - 1][0]); + $constraints_original = $m[0][0]; + if (! preg_match_all('/(?:^|,)\s*CONSTRAINT\s+.*?(?:,|\)\s*\))/imu', $constraints_original, $m)) { + continue; + } $constraints_array = $m[0]; foreach ($constraints_array as &$constraint) { $constraint = trim($constraint, ",\r\n"); diff --git a/tests/Mysql/MigrateDumpTest.php b/tests/Mysql/MigrateDumpTest.php index 2a82927..8ddc8bd 100644 --- a/tests/Mysql/MigrateDumpTest.php +++ b/tests/Mysql/MigrateDumpTest.php @@ -34,12 +34,20 @@ public function test_trimUnderscoresFromForeign() $sql = "KEY z_index, CONSTRAINT `__b_fk` FOREIGN KEY (`b`) REFERENCES `b` ON(`b`), CONSTRAINT `a_fk` FOREIGN KEY (`a`) REFERENCES `a` ON(`a`) +); +...KEY z2_index, + CONSTRAINT `__d_fk` FOREIGN KEY (`d`) REFERENCES `d` ON(`d`), + CONSTRAINT `c_fk` FOREIGN KEY (`c`) REFERENCES `c` ON(`c`) );"; $trimmed = MigrateDumpCommand::trimUnderscoresFromForeign($sql); $this->assertEquals( "KEY z_index, CONSTRAINT `a_fk` FOREIGN KEY (`a`) REFERENCES `a` ON(`a`), CONSTRAINT `b_fk` FOREIGN KEY (`b`) REFERENCES `b` ON(`b`) +); +...KEY z2_index, + CONSTRAINT `c_fk` FOREIGN KEY (`c`) REFERENCES `c` ON(`c`), + CONSTRAINT `d_fk` FOREIGN KEY (`d`) REFERENCES `d` ON(`d`) );", $trimmed );