From ed926e07fba7f9dfd2ea28892a42821bcdcc76a0 Mon Sep 17 00:00:00 2001 From: Ben Johnson Date: Thu, 17 Mar 2022 13:44:31 -0400 Subject: [PATCH] fix: "artisan db:wipe" command does not drop tables in all schemas Fixes #41483 --- .../Schema/Grammars/PostgresGrammar.php | 37 ++++++++++++++++--- .../Database/Schema/PostgresBuilder.php | 2 +- .../Database/DatabasePostgresBuilderTest.php | 24 ++++++------ 3 files changed, 45 insertions(+), 18 deletions(-) diff --git a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php index 603eb24def72..ee41538733bc 100755 --- a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php @@ -272,7 +272,7 @@ public function compileDropIfExists(Blueprint $blueprint, Fluent $command) */ public function compileDropAllTables($tables) { - return 'drop table "'.implode('","', $tables).'" cascade'; + return 'drop table '.implode(',', $this->escapeObjectReferences($tables)).' cascade'; } /** @@ -283,7 +283,7 @@ public function compileDropAllTables($tables) */ public function compileDropAllViews($views) { - return 'drop view "'.implode('","', $views).'" cascade'; + return 'drop view '.implode(',', $this->escapeObjectReferences($views)).' cascade'; } /** @@ -294,7 +294,7 @@ public function compileDropAllViews($views) */ public function compileDropAllTypes($types) { - return 'drop type "'.implode('","', $types).'" cascade'; + return 'drop type '.implode(',', $this->escapeObjectReferences($types)).' cascade'; } /** @@ -305,7 +305,7 @@ public function compileDropAllTypes($types) */ public function compileGetAllTables($searchPath) { - return "select tablename from pg_catalog.pg_tables where schemaname in ('".implode("','", (array) $searchPath)."')"; + return "select tablename, schemaname from pg_catalog.pg_tables where schemaname in ('".implode("','", (array) $searchPath)."')"; } /** @@ -316,7 +316,7 @@ public function compileGetAllTables($searchPath) */ public function compileGetAllViews($searchPath) { - return "select viewname from pg_catalog.pg_views where schemaname in ('".implode("','", (array) $searchPath)."')"; + return "select viewname, schemaname from pg_catalog.pg_views where schemaname in ('".implode("','", (array) $searchPath)."')"; } /** @@ -1070,4 +1070,31 @@ protected function modifyStoredAs(Blueprint $blueprint, Fluent $column) return " generated always as ({$column->storedAs}) stored"; } } + + /** + * Escape database object references consitently, schema-qualified or not. + * + * @param array $objects + * @return array + */ + protected function escapeObjectReferences(array $objects): array + { + $escapedObjects = []; + + foreach ($objects as $object) { + $parts = explode('.', $object); + + $newParts = []; + + array_walk($parts, function (&$part) use (&$newParts) { + $part = str_replace(['"',"'"], "", $part); + + $newParts[] = $part; + }); + + $escapedObjects[] = '"' . implode('"."', $parts) . '"'; + } + + return $escapedObjects; + } } diff --git a/src/Illuminate/Database/Schema/PostgresBuilder.php b/src/Illuminate/Database/Schema/PostgresBuilder.php index f0f866221185..08471c48d305 100755 --- a/src/Illuminate/Database/Schema/PostgresBuilder.php +++ b/src/Illuminate/Database/Schema/PostgresBuilder.php @@ -67,7 +67,7 @@ public function dropAllTables() foreach ($this->getAllTables() as $row) { $row = (array) $row; - $table = reset($row); + $table = '"' . $row['schemaname'] . '"."' . $row['tablename'] . '"'; if (! in_array($table, $excludedTables)) { $tables[] = $table; diff --git a/tests/Database/DatabasePostgresBuilderTest.php b/tests/Database/DatabasePostgresBuilderTest.php index 3cfd63d7ee7f..611f62384866 100644 --- a/tests/Database/DatabasePostgresBuilderTest.php +++ b/tests/Database/DatabasePostgresBuilderTest.php @@ -238,10 +238,10 @@ public function testDropAllTablesWhenSearchPathIsString() $connection->shouldReceive('getConfig')->with('dont_drop')->andReturn(['foo']); $grammar = m::mock(PostgresGrammar::class); $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); - $grammar->shouldReceive('compileGetAllTables')->with(['public'])->andReturn("select tablename from pg_catalog.pg_tables where schemaname in ('public')"); - $connection->shouldReceive('select')->with("select tablename from pg_catalog.pg_tables where schemaname in ('public')")->andReturn(['users']); - $grammar->shouldReceive('compileDropAllTables')->with(['users'])->andReturn('drop table "'.implode('","', ['users']).'" cascade'); - $connection->shouldReceive('statement')->with('drop table "'.implode('","', ['users']).'" cascade'); + $grammar->shouldReceive('compileGetAllTables')->with(['public'])->andReturn("select tablename, schemaname from pg_catalog.pg_tables where schemaname in ('public')"); + $connection->shouldReceive('select')->with("select tablename, schemaname from pg_catalog.pg_tables where schemaname in ('public')")->andReturn([['schemaname' => 'public', 'tablename' => 'users']]); + $grammar->shouldReceive('compileDropAllTables')->with(['"public"."users"'])->andReturn('drop table "public"."users" cascade'); + $connection->shouldReceive('statement')->with('drop table "public"."users" cascade'); $builder = $this->getBuilder($connection); $builder->dropAllTables(); @@ -255,10 +255,10 @@ public function testDropAllTablesWhenSearchPathIsStringOfMany() $connection->shouldReceive('getConfig')->with('dont_drop')->andReturn(['foo']); $grammar = m::mock(PostgresGrammar::class); $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); - $grammar->shouldReceive('compileGetAllTables')->with(['foouser', 'public', 'foo_bar-Baz.Áüõß'])->andReturn("select tablename from pg_catalog.pg_tables where schemaname in ('foouser','public','foo_bar-Baz.Áüõß')"); - $connection->shouldReceive('select')->with("select tablename from pg_catalog.pg_tables where schemaname in ('foouser','public','foo_bar-Baz.Áüõß')")->andReturn(['users', 'users']); - $grammar->shouldReceive('compileDropAllTables')->with(['users', 'users'])->andReturn('drop table "'.implode('","', ['users', 'users']).'" cascade'); - $connection->shouldReceive('statement')->with('drop table "'.implode('","', ['users', 'users']).'" cascade'); + $grammar->shouldReceive('compileGetAllTables')->with(['foouser', 'public', 'foo_bar-Baz.Áüõß'])->andReturn("select tablename, schemaname from pg_catalog.pg_tables where schemaname in ('foouser','public','foo_bar-Baz.Áüõß')"); + $connection->shouldReceive('select')->with("select tablename, schemaname from pg_catalog.pg_tables where schemaname in ('foouser','public','foo_bar-Baz.Áüõß')")->andReturn([['schemaname' => 'users', 'tablename' => 'users']]); + $grammar->shouldReceive('compileDropAllTables')->with(['"users"."users"'])->andReturn('drop table "users"."users" cascade'); + $connection->shouldReceive('statement')->with('drop table "users"."users" cascade'); $builder = $this->getBuilder($connection); $builder->dropAllTables(); @@ -277,10 +277,10 @@ public function testDropAllTablesWhenSearchPathIsArrayOfMany() $connection->shouldReceive('getConfig')->with('dont_drop')->andReturn(['foo']); $grammar = m::mock(PostgresGrammar::class); $connection->shouldReceive('getSchemaGrammar')->once()->andReturn($grammar); - $grammar->shouldReceive('compileGetAllTables')->with(['foouser', 'dev', 'test', 'spaced schema'])->andReturn("select tablename from pg_catalog.pg_tables where schemaname in ('foouser','dev','test','spaced schema')"); - $connection->shouldReceive('select')->with("select tablename from pg_catalog.pg_tables where schemaname in ('foouser','dev','test','spaced schema')")->andReturn(['users', 'users']); - $grammar->shouldReceive('compileDropAllTables')->with(['users', 'users'])->andReturn('drop table "'.implode('","', ['users', 'users']).'" cascade'); - $connection->shouldReceive('statement')->with('drop table "'.implode('","', ['users', 'users']).'" cascade'); + $grammar->shouldReceive('compileGetAllTables')->with(['foouser', 'dev', 'test', 'spaced schema'])->andReturn("select tablename, schemaname from pg_catalog.pg_tables where schemaname in ('foouser','dev','test','spaced schema')"); + $connection->shouldReceive('select')->with("select tablename, schemaname from pg_catalog.pg_tables where schemaname in ('foouser','dev','test','spaced schema')")->andReturn([['schemaname' => 'users', 'tablename' => 'users']]); + $grammar->shouldReceive('compileDropAllTables')->with(['"users"."users"'])->andReturn('drop table "users"."users" cascade'); + $connection->shouldReceive('statement')->with('drop table "users"."users" cascade'); $builder = $this->getBuilder($connection); $builder->dropAllTables();