Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[10.x] Add hasIndex() and minor Schema enhancements #49796

Merged
merged 4 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
59 changes: 59 additions & 0 deletions src/Illuminate/Database/Schema/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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.
*
Expand Down
2 changes: 1 addition & 1 deletion src/Illuminate/Database/Schema/ColumnDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
}

Expand Down
3 changes: 3 additions & 0 deletions src/Illuminate/Support/Facades/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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)
Expand Down
8 changes: 3 additions & 5 deletions tests/Database/DatabaseSQLiteSchemaGrammarTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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']
));
}
Expand Down
10 changes: 2 additions & 8 deletions tests/Database/DatabaseSchemaBuilderIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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()
Expand Down
16 changes: 15 additions & 1 deletion tests/Integration/Database/SchemaBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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()
Expand All @@ -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()
Expand Down