Skip to content

Commit

Permalink
Add refreshMaterializedView command
Browse files Browse the repository at this point in the history
  • Loading branch information
Gerych1984 committed Jan 19, 2024
1 parent be590ac commit aaff285
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 1 deletion.
56 changes: 56 additions & 0 deletions src/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@

namespace Yiisoft\Db\Pgsql;

use InvalidArgumentException;
use LogicException;
use Yiisoft\Db\Driver\Pdo\AbstractPdoCommand;

use function sprintf;

/**
* Implements a database command that can be executed with a PDO (PHP Data Object) database connection for PostgreSQL
* Server.
Expand All @@ -20,4 +24,56 @@ public function showDatabases(): array

return $this->setSql($sql)->queryColumn();
}

/**
* @see {https://www.postgresql.org/docs/current/sql-refreshmaterializedview.html}
*
* @param string $viewName
* @param bool|null $concurrently Add [ CONCURRENTLY ] to refresh command
* @param bool|null $withData Add [ WITH [ NO ] DATA ] to refresh command
* @return void
* @throws \Throwable
* @throws \Yiisoft\Db\Exception\Exception
* @throws \Yiisoft\Db\Exception\InvalidConfigException
*/
public function refreshMaterializedView(string $viewName, ?bool $concurrently = null, ?bool $withData = null): void
{
if ($concurrently || ($concurrently === null || $withData === null)) {

$tableSchema = $this->db->getTableSchema($viewName);

if ($tableSchema) {
$hasUnique = count($this->db->getSchema()->findUniqueIndexes($tableSchema)) > 0;
} else {
throw new InvalidArgumentException(
sprintf('"%s" not found in DB', $viewName)
);
}

if ($concurrently && !$hasUnique) {
throw new LogicException('CONCURRENTLY refresh is not allowed without unique index.');
}

$concurrently = $hasUnique;
}

$sql = 'REFRESH MATERIALIZED VIEW';

if ($concurrently) {

if ($withData === false) {
throw new LogicException('CONCURRENTLY and WITH NO DATA may not be specified together.');
}

$sql .= ' CONCURRENTLY';
}

$sql .= ' ' . $this->db->getQuoter()->quoteTableName($viewName);

if (is_bool($withData)) {
$sql .= ' WITH ' . ($withData ? 'DATA' : 'NO DATA');
}

$this->setSql($sql)->execute();
}
}
79 changes: 78 additions & 1 deletion tests/CommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@

namespace Yiisoft\Db\Pgsql\Tests;

use InvalidArgumentException;
use LogicException;
use Throwable;
use Yiisoft\Db\Exception\Exception;
use Yiisoft\Db\Exception\InvalidConfigException;
use Yiisoft\Db\Exception\NotSupportedException;
use Yiisoft\Db\Expression\JsonExpression;
use Yiisoft\Db\Pgsql\Command;
use Yiisoft\Db\Pgsql\Connection;
use Yiisoft\Db\Pgsql\Dsn;
use Yiisoft\Db\Pgsql\Driver;
use Yiisoft\Db\Pgsql\Dsn;
use Yiisoft\Db\Pgsql\Tests\Support\TestTrait;
use Yiisoft\Db\Tests\Common\CommonCommandTest;
use Yiisoft\Db\Tests\Support\DbHelper;
Expand Down Expand Up @@ -330,4 +333,78 @@ public function testShowDatabases(): void
$this->assertSame('pgsql:host=127.0.0.1;dbname=postgres;port=5432', $db->getDriver()->getDsn());
$this->assertSame(['yiitest'], $command->showDatabases());
}

public function testRefreshMaterializesView(): void
{
$db = $this->getConnection(true);
/** @var Command $command */
$command = $db->createCommand();

$command->refreshMaterializedView('mat_view_without_unique');

$this->expectException(LogicException::class);
$this->expectExceptionMessage('CONCURRENTLY refresh is not allowed without unique index.');
$command->refreshMaterializedView('mat_view_without_unique', true);
}

public static function materializedViewExceptionsDataProvider(): array
{
return [
[
'mat_view_without_unique',
true,
null,
LogicException::class,
'CONCURRENTLY refresh is not allowed without unique index.'
],

[
'mat_view_with_unique',
true,
false,
LogicException::class,
'CONCURRENTLY and WITH NO DATA may not be specified together.'
],

[
'mat_view_with_unique',
null,
false,
LogicException::class,
'CONCURRENTLY and WITH NO DATA may not be specified together.'
],

[
'not_exists_mat_view',
null,
null,
InvalidArgumentException::class,
'"not_exists_mat_view" not found in DB'
]
];
}

/**
* @dataProvider materializedViewExceptionsDataProvider
* @param string $viewName
* @param bool|null $concurrently
* @param bool|null $withData
* @param string $exception
* @param string $message
* @return void
* @throws Exception
* @throws InvalidConfigException
*/
public function testRefreshMaterializesViewExceptions(string $viewName, ?bool $concurrently, ?bool $withData, string $exception, string $message): void
{
$db = $this->getConnection(true);

/** @var Command $command */
$command = $db->createCommand();

$this->expectException($exception);
$this->expectExceptionMessage($message);

$command->refreshMaterializedView($viewName, $concurrently, $withData);
}
}
7 changes: 7 additions & 0 deletions tests/Support/Fixture/pgsql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -487,3 +487,10 @@ CREATE TABLE "test_composite_type"
"price_array2" "currency_money_composite"[][],
"range_price_col" "range_price_composite" DEFAULT '("(0,USD)","(100,USD)")'
);


DROP MATERIALIZED VIEW IF EXISTS "mat_view_without_unique";
DROP MATERIALIZED VIEW IF EXISTS "mat_view_with_unique";
CREATE MATERIALIZED VIEW "mat_view_without_unique" AS SELECT * FROM "test_composite_type";
CREATE MATERIALIZED VIEW "mat_view_with_unique" AS SELECT * FROM "test_composite_type";
CREATE UNIQUE INDEX "mat_view_with_unique_idx" ON "mat_view_with_unique" ("id");

0 comments on commit aaff285

Please sign in to comment.