From 14e8d313c3dc429e1ab9f5a542b34e0c84dbc208 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 24 Jan 2024 00:34:47 +0330 Subject: [PATCH 1/4] add hasIndex, getIndexListing and getTableListing --- src/Illuminate/Database/Schema/Builder.php | 59 +++++++++++++++++++ src/Illuminate/Support/Facades/Schema.php | 3 + .../DatabaseSQLiteSchemaGrammarTest.php | 8 +-- .../DatabaseSchemaBuilderIntegrationTest.php | 10 +--- .../Database/SchemaBuilderTest.php | 9 +++ 5 files changed, 76 insertions(+), 13 deletions(-) diff --git a/src/Illuminate/Database/Schema/Builder.php b/src/Illuminate/Database/Schema/Builder.php index 770c6c52655c..8340c64ac244 100755 --- a/src/Illuminate/Database/Schema/Builder.php +++ b/src/Illuminate/Database/Schema/Builder.php @@ -202,6 +202,16 @@ public function getTables() ); } + /** + * Get the table listing. + * + * @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 index listing 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/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..92d627a66ae9 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->assertTrue($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..175a3cc8d948 100644 --- a/tests/Integration/Database/SchemaBuilderTest.php +++ b/tests/Integration/Database/SchemaBuilderTest.php @@ -256,6 +256,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 +281,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() From 0ecc90496c68a78d848f54843e86359a1eee9131 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 24 Jan 2024 00:35:15 +0330 Subject: [PATCH 2/4] minor schema enhancements --- .../Database/Query/Processors/MySqlProcessor.php | 2 +- src/Illuminate/Database/Schema/ColumnDefinition.php | 2 +- .../Database/Schema/Grammars/PostgresGrammar.php | 2 +- tests/Integration/Database/SchemaBuilderTest.php | 7 ++++++- 4 files changed, 9 insertions(+), 4 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/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/tests/Integration/Database/SchemaBuilderTest.php b/tests/Integration/Database/SchemaBuilderTest.php index 175a3cc8d948..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(); From 347c1d9aecc0529c8af35aa380e2260f956c88f2 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 24 Jan 2024 01:00:52 +0330 Subject: [PATCH 3/4] fix tests --- tests/Database/DatabaseSQLiteSchemaGrammarTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Database/DatabaseSQLiteSchemaGrammarTest.php b/tests/Database/DatabaseSQLiteSchemaGrammarTest.php index 92d627a66ae9..409c28ccd637 100755 --- a/tests/Database/DatabaseSQLiteSchemaGrammarTest.php +++ b/tests/Database/DatabaseSQLiteSchemaGrammarTest.php @@ -173,7 +173,7 @@ public function testRenameIndex() $table->renameIndex('index1', 'index2'); }); - $this->assertTrue($schema->hasIndex('users', 'index1')); + $this->assertFalse($schema->hasIndex('users', 'index1')); $this->assertTrue(collect($schema->getIndexes('users'))->contains( fn ($index) => $index['name'] === 'index2' && $index['columns'] === ['name', 'email'] )); From 1797d52e672400cfce715c8154f58e1a143ef5d2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 24 Jan 2024 08:36:52 -0600 Subject: [PATCH 4/4] Update Builder.php --- src/Illuminate/Database/Schema/Builder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Database/Schema/Builder.php b/src/Illuminate/Database/Schema/Builder.php index 8340c64ac244..9cb687f97e8a 100755 --- a/src/Illuminate/Database/Schema/Builder.php +++ b/src/Illuminate/Database/Schema/Builder.php @@ -203,7 +203,7 @@ public function getTables() } /** - * Get the table listing. + * Get the names of the tables that belong to the database. * * @return array */ @@ -381,7 +381,7 @@ public function getIndexes($table) } /** - * Get the index listing for a given table. + * Get the names of the indexes for a given table. * * @param string $table * @return array