Skip to content

Commit

Permalink
Added support deleting UNIQUE partial indexes (#22)
Browse files Browse the repository at this point in the history
Added support for deleting unique indexes with WHERE conditions.

By default, if the created unique index contains no conditions, PostgresSQL automatically creates a Constraint for it, for example:

CREATE UNIQUE INDEX CONCURRENTLY examples_new_col_idx ON examples (new_col);
ALTER TABLE examples
    ADD CONSTRAINT examples_unique_constraint USING INDEX examples_new_col_idx;
If the unique index contains WHERE conditions, then such a Constraint will not be created, because PostgreSQL doesn't
define a partial (ie conditional) UNIQUE constraint.

When trying to delete such a partial unique index, we get the error "Unique Constraint not found", so the standard command to remove $table->dropUnique() does not fit, the command $table->dropIndex() is suitable, but if pass the array there, then the index name will be generated as for a regular index with the suffix _index, instead of _unique, which we need.

And manually generating the index name is wrong.

This Merge Request solves these problems.
  • Loading branch information
pvsaintpe authored and lazeevv committed Aug 6, 2019
1 parent 9b419f2 commit 7e5312a
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 8 deletions.
25 changes: 23 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Schema::create('table', function (Blueprint $table) {
Schema::table('table', function (Blueprint $table) {
$table
->string('number')
->using("('[' || number || ']')::character varyiing")
->using("('[' || number || ']')::character varying")
->change();
});
```
Expand All @@ -61,7 +61,7 @@ Schema::dropView('active_users')
// Schema methods:
Schema::create('users', function (Blueprint $table) {
$table
->createView('active_users', , "SELECT * FROM users WHERE active = 1")
->createView('active_users', "SELECT * FROM users WHERE active = 1")
->materialize();
});
```
Expand All @@ -77,6 +77,27 @@ Schema::create('table', function (Blueprint $table) {
});
```

If you want to delete partial unique index, use this method:
```php
Schema::create('table', function (Blueprint $table) {
$table->dropUniquePartial(['code']);
});
```

`$table->dropUnique()` doesn't work for Partial Unique Indexes, because PostgreSQL doesn't
define a partial (ie conditional) UNIQUE constraint. If you try to delete such a Partial Unique
Index you will get an error.

```SQL
CREATE UNIQUE INDEX CONCURRENTLY examples_new_col_idx ON examples (new_col);
ALTER TABLE examples
ADD CONSTRAINT examples_unique_constraint USING INDEX examples_new_col_idx;
```

When you create a unique index without conditions, PostgresSQL will create Unique Constraint
automatically for you, and when you try to delete such an index, Constraint will be deleted
first, then Unique Index.

### Partitions

Support for attaching and detaching partitions.
Expand Down
5 changes: 5 additions & 0 deletions src/Schema/Blueprint.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ public function uniquePartial($columns, ?string $index = null, ?string $algorith
);
}

public function dropUniquePartial($index): Fluent
{
return $this->dropIndexCommand('dropIndex', 'unique', $index);
}

public function hasIndex($index, bool $unique = false): bool
{
if (is_array($index)) {
Expand Down
1 change: 1 addition & 0 deletions tests/Functional/Helpers/ColumnAssertions.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ protected function assertTypeColumn(string $table, string $column, string $expec
{
$this->assertSame($expected, Schema::getColumnType($table, $column));
}

private function getCommentListing(string $table, string $column)
{
$definition = DB::selectOne(
Expand Down
31 changes: 30 additions & 1 deletion tests/Functional/Helpers/IndexAssertions.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ protected function seeIndex(string $index): void
$this->assertNotNull($this->getIndexListing($index));
}

protected function notSeeIndex(string $index): void
{
$this->assertNull($this->getIndexListing($index));
}

protected function assertSameIndex(string $index, string $expectedDef): void
{
$definition = $this->getIndexListing($index);
Expand All @@ -32,10 +37,34 @@ protected function assertRegExpIndex(string $index, string $expectedDef): void
$this->seeIndex($index);
$this->assertRegExp($expectedDef, $definition);
}

protected function dontSeeConstraint(string $table, string $index): void
{
$this->assertFalse($this->existConstraintOnTable($table, $index));
}
protected function seeConstraint(string $table, string $index): void
{
$this->assertTrue($this->existConstraintOnTable($table, $index));
}

private function getIndexListing($index): ?string
{
$definition = DB::selectOne('SELECT indexdef FROM pg_indexes WHERE indexname = ?', [$index]);
$definition = DB::selectOne('SELECT * FROM pg_indexes WHERE indexname = ?', [$index]);

return $definition ? $definition->indexdef : null;
}

private function existConstraintOnTable(string $table, string $index): bool
{
$definition = DB::selectOne('
SELECT c.conname
FROM pg_constraint c
LEFT JOIN pg_class t ON c.conrelid = t.oid
LEFT JOIN pg_class t2 ON c.confrelid = t2.oid
WHERE t.relname = ? AND c.conname = ?;
',
[$table, $index]
);
return $definition ? true : false;
}
}
21 changes: 16 additions & 5 deletions tests/Functional/Schema/CreateIndexTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
use Illuminate\Support\Facades\Schema;
use Umbrellio\Postgres\Schema\Blueprint;
use Umbrellio\Postgres\Tests\Functional\Helpers\IndexAssertions;
use Umbrellio\Postgres\Tests\Functional\Helpers\TableAssertions;
use Umbrellio\Postgres\Tests\FunctionalTestCase;

class CreateIndexTest extends FunctionalTestCase
{
use DatabaseTransactions, IndexAssertions;
use DatabaseTransactions, IndexAssertions, TableAssertions;

/** @test */
public function createIndexIfNotExists(): void
Expand All @@ -28,7 +29,7 @@ public function createIndexIfNotExists(): void
}
});

$this->assertTrue(Schema::hasTable('test_table'));
$this->seeTable('test_table');

Schema::table('test_table', function (Blueprint $table) {
if (!$table->hasIndex(['name'], true)) {
Expand All @@ -43,7 +44,7 @@ public function createIndexIfNotExists(): void
* @test
* @dataProvider provideIndexes
*/
public function createPartialUniqueWithNull(string $expected, Closure $callback): void
public function createPartialUnique(string $expected, Closure $callback): void
{
Schema::create('test_table', function (Blueprint $table) use ($callback) {
$table->increments('id');
Expand All @@ -57,8 +58,18 @@ public function createPartialUniqueWithNull(string $expected, Closure $callback)
$callback($table);
});

$this->assertTrue(Schema::hasTable('test_table'));
$this->seeTable('test_table');
$this->assertRegExpIndex('test_table_name_unique', '/' . $this->getDummyIndex() . $expected . '/');

Schema::table('test_table', function (Blueprint $table) {
if (!$this->existConstraintOnTable($table->getTable(), 'test_table_name_unique')) {
$table->dropUniquePartial(['name']);
} else {
$table->dropUnique(['name']);
}
});

$this->notSeeIndex('test_table_name_unique');
}

/** @test */
Expand All @@ -68,7 +79,7 @@ public function createSpecifyIndex(): void
$table->string('name')->index('specify_index_name');
});

$this->assertTrue(Schema::hasTable('test_table'));
$this->seeTable('test_table');

$this->assertRegExpIndex(
'specify_index_name',
Expand Down

0 comments on commit 7e5312a

Please sign in to comment.