From be5afef96d774d7b7ce56ebd27b36e3bbc34f117 Mon Sep 17 00:00:00 2001 From: Matthew Spero Date: Fri, 10 Nov 2023 12:06:33 -0600 Subject: [PATCH 1/3] Batch pt-online-schema-change statements --- src/BatchableBlueprint.php | 25 +++++++++ .../PtOnlineSchemaChangeConnection.php | 55 ++++++++++++++----- src/ServiceProvider.php | 14 +++++ .../PtOnlineSchemaChangeConnectionTest.php | 20 +++++++ 4 files changed, 101 insertions(+), 13 deletions(-) create mode 100644 src/BatchableBlueprint.php diff --git a/src/BatchableBlueprint.php b/src/BatchableBlueprint.php new file mode 100644 index 0000000..07d560e --- /dev/null +++ b/src/BatchableBlueprint.php @@ -0,0 +1,25 @@ +toSql($connection, $grammar); + return !empty($statements) ? $connection->statements($statements) : null; + } + + return parent::build($connection, $grammar); + } +} diff --git a/src/Connections/PtOnlineSchemaChangeConnection.php b/src/Connections/PtOnlineSchemaChangeConnection.php index ea72781..5a0b2f3 100644 --- a/src/Connections/PtOnlineSchemaChangeConnection.php +++ b/src/Connections/PtOnlineSchemaChangeConnection.php @@ -13,20 +13,22 @@ class PtOnlineSchemaChangeConnection extends BaseConnection */ public function statement($query, $bindings = []) { - $table = $this->extractTableFromQuery($query); + return $this->runQueries([$query]); + } - return $this->runProcess(array_merge( - [ - 'pt-online-schema-change', - $this->isPretending() ? '--dry-run' : '--execute', - ], - $this->getAdditionalParameters(), - [ - '--alter', - $this->cleanQuery($query), - $this->getAuthString($table), - ] - )); + /** + * A custom connection method called by our custom schema builder to help batch + * operations on the cloned table. + * + * @see \Daursu\ZeroDowntimeMigration\BatchableBlueprint + * + * @param string $query + * @param array $bindings + * @return bool|int + */ + public function statements($queries, $bindings = []) + { + return $this->runQueries($queries); } /** @@ -60,4 +62,31 @@ protected function maskSensitiveInformation(array $command): string return preg_replace('/('.preg_quote($this->getConfig('username'), '/').'),/', '*****,', $config); })->implode(' '); } + + /** + * @param string[] $queries + * @return bool|int + */ + protected function runQueries($queries) + { + $table = $this->extractTableFromQuery($queries[0]); + + $cleanQueries = []; + foreach($queries as $query) { + $cleanQueries[] = $this->cleanQuery($query); + } + + return $this->runProcess(array_merge( + [ + 'pt-online-schema-change', + $this->isPretending() ? '--dry-run' : '--execute', + ], + $this->getAdditionalParameters(), + [ + '--alter', + implode(', ', $cleanQueries), + $this->getAuthString($table), + ] + )); + } } diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index d1fdac8..ca7808c 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -6,7 +6,9 @@ use Daursu\ZeroDowntimeMigration\Connections\PtOnlineSchemaChangeConnection; use Illuminate\Database\Connection; use Illuminate\Database\Connectors\MySqlConnector; +use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\ServiceProvider as IlluminateServiceProvider; +use ReflectionClass; class ServiceProvider extends IlluminateServiceProvider { @@ -42,5 +44,17 @@ public function register() $this->app->bind('db.connector.gh-ost', function () { return new MySqlConnector; }); + + $this->app->bind(Blueprint::class, function ($app, $args = []) { + return $this->createInstance(BatchableBlueprint::class, $args); + }); + } + + private function createInstance(string $class, array $args) + { + return call_user_func_array( + [new ReflectionClass($class), 'newInstance'], + $args + ); } } diff --git a/tests/Unit/PtOnlineSchemaChangeConnectionTest.php b/tests/Unit/PtOnlineSchemaChangeConnectionTest.php index c0f641c..9be431e 100644 --- a/tests/Unit/PtOnlineSchemaChangeConnectionTest.php +++ b/tests/Unit/PtOnlineSchemaChangeConnectionTest.php @@ -83,6 +83,26 @@ public function testItRemovesAlterTableStatementFromTheQuery() $connection->statement($query); } + public function testItConcatenatesMultipleStatements() + { + $queries = [ + "alter table `users` add `middle_name` varchar(255)", + "alter table `users` drop foreign key `created_by_foreign`", + ]; + $connection = $this->getConnectionWithMockedProcess(); + + $connection->expects($this->once()) + ->method('runProcess') + ->with($this->callback(function ($command) { + $command = implode(' ', $command); + return Str::contains($command, '--alter add `middle_name` varchar(255), '. + 'drop foreign key `created_by_foreign`'); + })) + ->willReturn(0); + + $connection->statements($queries); + } + public function testAdditionalOptionsAreLoadedIn() { $query = 'alter table `users` ADD `email` varchar(255)'; From 23d36ce4994f2114082e2190c64f908826ee5d29 Mon Sep 17 00:00:00 2001 From: Matthew Spero Date: Thu, 14 Dec 2023 17:33:38 -0600 Subject: [PATCH 2/3] Use newer unpacking syntax --- src/ServiceProvider.php | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index ca7808c..570955d 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -46,15 +46,7 @@ public function register() }); $this->app->bind(Blueprint::class, function ($app, $args = []) { - return $this->createInstance(BatchableBlueprint::class, $args); + return new BatchableBlueprint(...$args); }); } - - private function createInstance(string $class, array $args) - { - return call_user_func_array( - [new ReflectionClass($class), 'newInstance'], - $args - ); - } } From f88daf27cfe190d066c8238f39a1e4ae4ff9359b Mon Sep 17 00:00:00 2001 From: Matthew Spero Date: Thu, 14 Dec 2023 19:07:52 -0600 Subject: [PATCH 3/3] Remove unused argument + update doc-block --- src/Connections/PtOnlineSchemaChangeConnection.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Connections/PtOnlineSchemaChangeConnection.php b/src/Connections/PtOnlineSchemaChangeConnection.php index 5a0b2f3..c385da5 100644 --- a/src/Connections/PtOnlineSchemaChangeConnection.php +++ b/src/Connections/PtOnlineSchemaChangeConnection.php @@ -22,11 +22,10 @@ public function statement($query, $bindings = []) * * @see \Daursu\ZeroDowntimeMigration\BatchableBlueprint * - * @param string $query - * @param array $bindings + * @param string[] $queries * @return bool|int */ - public function statements($queries, $bindings = []) + public function statements($queries) { return $this->runQueries($queries); }