From 8fd5c3586c3b76c716c33b20df5b4851eeca6f0f Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 24 Jan 2024 18:11:29 +0330 Subject: [PATCH] [10.x] Add `hasIndex()` and minor Schema enhancements (#49796) * add hasIndex, getIndexListing and getTableListing * minor schema enhancements * fix tests * Update Builder.php --------- Co-authored-by: Taylor Otwell --- .../Query/Processors/MySqlProcessor.php | 2 +- src/Illuminate/Database/Schema/Builder.php | 59 +++++++++++++++++++ .../Database/Schema/ColumnDefinition.php | 2 +- .../Schema/Grammars/PostgresGrammar.php | 2 +- src/Illuminate/Support/Facades/Schema.php | 3 + .../DatabaseSQLiteSchemaGrammarTest.php | 8 +-- .../DatabaseSchemaBuilderIntegrationTest.php | 10 +--- .../Database/SchemaBuilderTest.php | 16 ++++- 8 files changed, 85 insertions(+), 17 deletions(-) diff --git a/src/Illuminate/Database/Query/Processors/MySqlProcessor.php b/src/Illuminate/Database/Query/Processors/MySqlProcessor.php index 091fc80b52b0..07553c39195b 100644 --- a/src/Illuminate/Database/Query/Processors/MySqlProcessor.php +++ b/src/Illuminate/Database/Query/Processors/MySqlProcessor.php @@ -38,7 +38,7 @@ public function processColumns($results) 'nullable' => $result->nullable === 'YES', 'default' => $result->default, 'auto_increment' => $result->extra === 'auto_increment', - 'comment' => $result->comment, + 'comment' => $result->comment ?: null, ]; }, $results); } diff --git a/src/Illuminate/Database/Schema/Builder.php b/src/Illuminate/Database/Schema/Builder.php index 770c6c52655c..9cb687f97e8a 100755 --- a/src/Illuminate/Database/Schema/Builder.php +++ b/src/Illuminate/Database/Schema/Builder.php @@ -202,6 +202,16 @@ public function getTables() ); } + /** + * Get the names of the tables that belong to the database. + * + * @return array + */ + public function getTableListing() + { + return array_column($this->getTables(), 'name'); + } + /** * Get the views that belong to the database. * @@ -370,6 +380,55 @@ public function getIndexes($table) ); } + /** + * Get the names of the indexes for a given table. + * + * @param string $table + * @return array + */ + public function getIndexListing($table) + { + return array_column($this->getIndexes($table), 'name'); + } + + /** + * Determine if the given table has a given index. + * + * @param string $table + * @param string|array $index + * @param string|null $type + * @return bool + */ + public function hasIndex($table, $index, $type = null) + { + $type = is_null($type) ? $type : strtolower($type); + + if (is_array($index)) { + sort($index); + } + + foreach ($this->getIndexes($table) as $value) { + $typeMatches = is_null($type) + || ($type === 'primary' && $value['primary']) + || ($type === 'unique' && $value['unique']) + || $type === $value['type']; + + if ($value['name'] === $index && $typeMatches) { + return true; + } + + if (is_array($index)) { + sort($value['columns']); + + if ($value['columns'] === $index && $typeMatches) { + return true; + } + } + } + + return false; + } + /** * Get the foreign keys for a given table. * diff --git a/src/Illuminate/Database/Schema/ColumnDefinition.php b/src/Illuminate/Database/Schema/ColumnDefinition.php index 51265ac4213e..1a7e638836b2 100644 --- a/src/Illuminate/Database/Schema/ColumnDefinition.php +++ b/src/Illuminate/Database/Schema/ColumnDefinition.php @@ -15,7 +15,7 @@ * @method $this default(mixed $value) Specify a "default" value for the column * @method $this first() Place the column "first" in the table (MySQL) * @method $this from(int $startingValue) Set the starting value of an auto-incrementing field (MySQL / PostgreSQL) - * @method $this generatedAs(string|Expression $expression = null) Create a SQL compliant identity column (PostgreSQL) + * @method $this generatedAs(string|\Illuminate\Database\Query\Expression $expression = null) Create a SQL compliant identity column (PostgreSQL) * @method $this index(string $indexName = null) Add an index * @method $this invisible() Specify that the column should be invisible to "SELECT *" (MySQL) * @method $this nullable(bool $value = true) Allow NULL values to be inserted into the column diff --git a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php index acc9c5800715..6119607abd76 100755 --- a/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php +++ b/src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php @@ -87,7 +87,7 @@ public function compileTables() { return 'select c.relname as name, n.nspname as schema, pg_total_relation_size(c.oid) as size, ' ."obj_description(c.oid, 'pg_class') as comment from pg_class c, pg_namespace n " - ."where c.relkind in ('r', 'p') and n.oid = c.relnamespace and n.nspname not in ('pg_catalog', 'information_schema')" + ."where c.relkind in ('r', 'p') and n.oid = c.relnamespace and n.nspname not in ('pg_catalog', 'information_schema') " .'order by c.relname'; } diff --git a/src/Illuminate/Support/Facades/Schema.php b/src/Illuminate/Support/Facades/Schema.php index 8aa0eb900a55..5ac399ac1b9a 100755 --- a/src/Illuminate/Support/Facades/Schema.php +++ b/src/Illuminate/Support/Facades/Schema.php @@ -12,6 +12,7 @@ * @method static bool dropDatabaseIfExists(string $name) * @method static bool hasTable(string $table) * @method static bool hasView(string $view) + * @method static bool hasIndex(string $table, string|array $index, string|null $type = null) * @method static array getTables() * @method static array getViews() * @method static array getTypes() @@ -20,7 +21,9 @@ * @method static void whenTableHasColumn(string $table, string $column, \Closure $callback) * @method static void whenTableDoesntHaveColumn(string $table, string $column, \Closure $callback) * @method static string getColumnType(string $table, string $column, bool $fullDefinition = false) + * @method static array getTableListing() * @method static array getColumnListing(string $table) + * @method static array getIndexListing(string $table) * @method static array getColumns(string $table) * @method static array getIndexes(string $table) * @method static array getForeignKeys(string $table) diff --git a/tests/Database/DatabaseSQLiteSchemaGrammarTest.php b/tests/Database/DatabaseSQLiteSchemaGrammarTest.php index 60339647c0f9..409c28ccd637 100755 --- a/tests/Database/DatabaseSQLiteSchemaGrammarTest.php +++ b/tests/Database/DatabaseSQLiteSchemaGrammarTest.php @@ -164,7 +164,7 @@ public function testRenameIndex() $table->index(['name', 'email'], 'index1'); }); - $indexes = array_column($schema->getIndexes('users'), 'name'); + $indexes = $schema->getIndexListing('users'); $this->assertContains('index1', $indexes); $this->assertNotContains('index2', $indexes); @@ -173,10 +173,8 @@ public function testRenameIndex() $table->renameIndex('index1', 'index2'); }); - $indexes = $schema->getIndexes('users'); - - $this->assertNotContains('index1', array_column($indexes, 'name')); - $this->assertTrue(collect($indexes)->contains( + $this->assertFalse($schema->hasIndex('users', 'index1')); + $this->assertTrue(collect($schema->getIndexes('users'))->contains( fn ($index) => $index['name'] === 'index2' && $index['columns'] === ['name', 'email'] )); } diff --git a/tests/Database/DatabaseSchemaBuilderIntegrationTest.php b/tests/Database/DatabaseSchemaBuilderIntegrationTest.php index ce261ab4e94d..80734d25fef8 100644 --- a/tests/Database/DatabaseSchemaBuilderIntegrationTest.php +++ b/tests/Database/DatabaseSchemaBuilderIntegrationTest.php @@ -87,10 +87,7 @@ public function testHasColumnAndIndexWithPrefixIndexDisabled() $table->string('name')->index(); }); - $this->assertContains( - 'table1_name_index', - array_column($this->db->connection()->getSchemaBuilder()->getIndexes('table1'), 'name') - ); + $this->assertTrue($this->db->connection()->getSchemaBuilder()->hasIndex('table1', 'table1_name_index')); } public function testHasColumnAndIndexWithPrefixIndexEnabled() @@ -107,10 +104,7 @@ public function testHasColumnAndIndexWithPrefixIndexEnabled() $table->string('name')->index(); }); - $this->assertContains( - 'example_table1_name_index', - array_column($this->db->connection()->getSchemaBuilder()->getIndexes('table1'), 'name') - ); + $this->assertTrue($this->db->connection()->getSchemaBuilder()->hasIndex('table1', 'example_table1_name_index')); } public function testDropColumnWithTablePrefix() diff --git a/tests/Integration/Database/SchemaBuilderTest.php b/tests/Integration/Database/SchemaBuilderTest.php index fbd42e13c489..7a75d62b5fe0 100644 --- a/tests/Integration/Database/SchemaBuilderTest.php +++ b/tests/Integration/Database/SchemaBuilderTest.php @@ -193,15 +193,20 @@ public function testGetAndDropTypes() DB::statement("create type enum_foo as enum ('new', 'open', 'closed')"); DB::statement('create type range_foo as range (subtype = float8)'); DB::statement('create domain domain_foo as text'); + DB::statement('create type base_foo'); + DB::statement("create function foo_in(cstring) returns base_foo language internal immutable strict parallel safe as 'int2in'"); + DB::statement("create function foo_out(base_foo) returns cstring language internal immutable strict parallel safe as 'int2out'"); + DB::statement('create type base_foo (input = foo_in, output = foo_out)'); $types = Schema::getTypes(); - $this->assertCount(11, $types); + $this->assertCount(13, $types); $this->assertTrue(collect($types)->contains(fn ($type) => $type['name'] === 'pseudo_foo' && $type['type'] === 'pseudo' && ! $type['implicit'])); $this->assertTrue(collect($types)->contains(fn ($type) => $type['name'] === 'comp_foo' && $type['type'] === 'composite' && ! $type['implicit'])); $this->assertTrue(collect($types)->contains(fn ($type) => $type['name'] === 'enum_foo' && $type['type'] === 'enum' && ! $type['implicit'])); $this->assertTrue(collect($types)->contains(fn ($type) => $type['name'] === 'range_foo' && $type['type'] === 'range' && ! $type['implicit'])); $this->assertTrue(collect($types)->contains(fn ($type) => $type['name'] === 'domain_foo' && $type['type'] === 'domain' && ! $type['implicit'])); + $this->assertTrue(collect($types)->contains(fn ($type) => $type['name'] === 'base_foo' && $type['type'] === 'base' && ! $type['implicit'])); Schema::dropAllTypes(); $types = Schema::getTypes(); @@ -256,6 +261,10 @@ public function testGetIndexes() && ! $indexes[0]['unique'] && ! $indexes[0]['primary'] ); + $this->assertTrue(Schema::hasIndex('foo', 'my_index')); + $this->assertTrue(Schema::hasIndex('foo', ['bar'])); + $this->assertFalse(Schema::hasIndex('foo', 'my_index', 'primary')); + $this->assertFalse(Schema::hasIndex('foo', ['bar'], 'unique')); } public function testGetUniqueIndexes() @@ -277,6 +286,11 @@ public function testGetUniqueIndexes() $this->assertTrue(collect($indexes)->contains( fn ($index) => $index['name'] === 'foo_baz_bar_unique' && $index['columns'] === ['baz', 'bar'] && $index['unique'] )); + $this->assertTrue(Schema::hasIndex('foo', 'foo_baz_bar_unique')); + $this->assertTrue(Schema::hasIndex('foo', 'foo_baz_bar_unique', 'unique')); + $this->assertTrue(Schema::hasIndex('foo', ['bar', 'baz'])); + $this->assertTrue(Schema::hasIndex('foo', ['bar', 'baz'], 'unique')); + $this->assertFalse(Schema::hasIndex('foo', ['bar', 'baz'], 'primary')); } public function testGetIndexesWithCompositeKeys()