8000 Adding support for the pgsql triggers · symfony/symfony@284d020 · GitHub
[go: up one dir, main page]

Skip to content

Commit 284d020

Browse files
committed
Adding support for the pgsql triggers
1 parent bf481f1 commit 284d020

File tree

6 files changed

+168
-4
lines changed

6 files changed

+168
-4
lines changed

src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/MessengerTransportDoctrineSchemaSubscriberTest.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212
namespace Symfony\Component\Messenger\Bridge\Doctrine\Tests\Transport;
1313

1414
use Doctrine\DBAL\Connection;
15+
use Doctrine\DBAL\Event\SchemaCreateTableEventArgs;
16+
use Doctrine\DBAL\Platforms\AbstractPlatform;
1517
use Doctrine\DBAL\Schema\Schema;
18+
use Doctrine\DBAL\Schema\Table;
1619
use Doctrine\ORM\EntityManagerInterface;
1720
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
1821
use PHPUnit\Framework\TestCase;
@@ -43,4 +46,54 @@ public function testPostGenerateSchema()
4346
$subscriber = new MessengerTransportDoctrineSchemaSubscriber([$doctrineTransport, $otherTransport]);
4447
$subscriber->postGenerateSchema($event);
4548
}
49+
50+
public function testOnSchemaCreateTable()
51+
{
52+
$platform = $this->createMock(AbstractPlatform::class);
53+
$table = new Table('queue_table');
54+
$event = new SchemaCreateTableEventArgs($table, [], [], $platform);
55+
56+
$otherTransport = $this->createMock(TransportInterface::class);
57+
$otherTransport->expects($this->never())
58+
->method($this->anything());
59+
60+
$doctrineTransport = $this->createMock(DoctrineTransport::class);
61+
$doctrineTransport->expects($this->once())
62+
->method('getExtraSetupSql')
63+
->with($table)
64+
->willReturn('ALTER TABLE pizza ADD COLUMN extra_cheese boolean');
65+
66+
// we use the platform to generate the full create table sql
67+
$platform->expects($this->once())
68+
->method('getCreateTableSQL')
69+
->with($table)
70+
->willReturn('CREATE TABLE pizza (id integer NOT NULL)');
71+
72+
$subscriber = new MessengerTransportDoctrineSchemaSubscriber([$otherTransport, $doctrineTransport]);
73+
$subscriber->onSchemaCreateTable($event);
74+
$this->assertTrue($event->isDefaultPrevented());
75+
$this->assertSame([
76+
'CREATE TABLE pizza (id integer NOT NULL)',
77+
'ALTER TABLE pizza ADD COLUMN extra_cheese boolean',
78+
], $event->getSql());
79+
}
80+
81+
public function testOnSchemaCreateTableNoExtraSql()
82+
{
83+
$platform = $this->createMock(AbstractPlatform::class);
84+
$table = new Table('queue_table');
85+
$event = new SchemaCreateTableEventArgs($table, [], [], $platform);
86+
87+
$do 629A ctrineTransport = $this->createMock(DoctrineTransport::class);
88+
$doctrineTransport->expects($this->once())
89+
->method('getExtraSetupSql')
90+
->willReturn(null);
91+
92+
$platform->expects($this->never())
93+
->method('getCreateTableSQL');
94+
95+
$subscriber = new MessengerTransportDoctrineSchemaSubscriber([$doctrineTransport]);
96+
$subscriber->onSchemaCreateTable($event);
97+
$this->assertFalse($event->isDefaultPrevented());
98+
}
4699
}

src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/PostgreSqlConnectionTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Messenger\Bridge\Doctrine\Tests\Transport;
1313

1414
use Doctrine\DBAL\Schema\Synchronizer\SchemaSynchronizer;
15+
use Doctrine\DBAL\Schema\Table;
1516
use PHPUnit\Framework\TestCase;
1617
use Symfony\Component\Messenger\Bridge\Doctrine\Transport\PostgreSqlConnection;
1718

@@ -43,4 +44,24 @@ public function testUnserialize()
4344
$connection = new PostgreSqlConnection([], $driverConnection, $schemaSynchronizer);
4445
$connection->__wakeup();
4546
}
47+
48+
public function testGetExtraSetupSql()
49+
{
50+
$driverConnection = $this->createMock(\Doctrine\DBAL\Connection::class);
51+
$connection = new PostgreSqlConnection(['table_name' => 'queue_table'], $driverConnection);
52+
53+
$table = new Table('queue_table');
54+
$table->addOption('_symfony_messenger_table_name', 'queue_table');
55+
$this->assertStringContainsString('CREATE TRIGGER', $connection->getExtraSetupSql($table));
56+
}
57+
58+
public function testGetExtraSetupSqlWrongTable()
59+
{
60+
$driverConnection = $this->createMock(\Doctrine\DBAL\Connection::class);
61+
$connection = new PostgreSqlConnection(['table_name' => 'queue_table'], $driverConnection);
62+
63+
$table = new Table('queue_table');
64+
// don't set the _symfony_messenger_table_name option
65+
$this->assertNull($connection->getExtraSetupSql($table));
66+
}
4667
}

src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/Connection.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
*/
3535
class Connection implements ResetInterface
3636
{
37+
protected const TABLE_OPTION_NAME = '_symfony_messenger_table_name';
38+
3739
protected const DEFAULT_OPTIONS = [
3840
'table_name' => 'messenger_messages',
3941
'queue_name' => 'default',
@@ -291,6 +293,9 @@ public function find($id): ?array
291293
return false === $data ? null : $this->decodeEnvelopeHeaders($data);
292294
}
293295

296+
/**
297+
* @internal
298+
*/
294299
public function configureSchema(Schema $schema, DBALConnection $dbalConnection): void
295300
{
296301
// only update the schema for this connection
@@ -305,6 +310,14 @@ public function configureSchema(Schema $schema, DBALConnection $dbalConnection):
305310
$this->addTableToSchema($schema);
306311
}
307312

313+
/**
314+
* @internal
315+
*/
316+
public function getExtraSetupSql(Table $table): ?string
317+
{
318+
return null;
319+
}
320+
308321
private function createAvailableMessagesQueryBuilder(): QueryBuilder
309322
{
310323
$now = new \DateTime();
@@ -364,6 +377,8 @@ private function getSchema(): Schema
364377
private function addTableToSchema(Schema $schema): void
365378
{
366379
$table = $schema->createTable($this->configuration['table_name']);
380+
// add an internal option to mark that we created this & the non-namespaced table name
381+
$table->addOption(self::TABLE_OPTION_NAME, $this->configuration['table_name']);
367382
$table->addColumn('id', self::$useDeprecatedConstants ? Type::BIGINT : Types::BIGINT)
368383
->setAutoincrement(true)
369384
->setNotnull(true);

src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/DoctrineTransport.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Doctrine\DBAL\Connection as DbalConnection;
1515
use Doctrine\DBAL\Schema\Schema;
16+
use Doctrine\DBAL\Schema\Table;
1617
use Symfony\Component\Messenger\Envelope;
1718
use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface;
1819
use Symfony\Component\Messenger\Transport\Receiver\MessageCountAwareInterface;
@@ -102,12 +103,24 @@ public function setup(): void
102103

103104
/**
104105
* Adds the Table to the Schema if this transport uses this connection.
106+
*
107+
* @internal
105108
*/
106109
public function configureSchema(Schema $schema, DbalConnection $dbalConnection): void
107110
{
108111
$this->connection->configureSchema($schema, $dbalConnection);
109112
}
110113

114+
/**
115+
* Adds extra SQL if the given table was created by the Connection.
116+
*
117+
* @internal
118+
*/
119+
public function getExtraSetupSql(Table $table): ?string
120+
{
121+
return $this->connection->getExtraSetupSql($table);
122+
}
123+
111124
private function getReceiver(): DoctrineReceiver
112125
{
113126
return $this->receiver = new DoctrineReceiver($this->connection, $this->serializer);

src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/MessengerTransportDoctrineSchemaSubscriber.php

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
namespace Symfony\Component\Messenger\Bridge\Doctrine\Transport;
1313

1414
use Doctrine\Common\EventSubscriber;
15+
use Doctrine\DBAL\Event\SchemaCreateTableEventArgs;
16+
use Doctrine\DBAL\Events;
1517
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
1618
use Doctrine\ORM\Tools\ToolEvents;
1719
use Symfony\Component\Messenger\Transport\TransportInterface;
@@ -23,6 +25,8 @@
2325
*/
2426
final class MessengerTransportDoctrineSchemaSubscriber implements EventSubscriber
2527
{
28+
private const PROCESSING_TABLE_FLAG = self::class.':processing';
29+
2630
private $transports;
2731

2832
/**
@@ -33,7 +37,7 @@ public function __construct(iterable $transports)
3337
$this->transports = $transports;
3438
}
3539

36-
public function postGenerateSchema(GenerateSchemaEventArgs $event)
40+
public function postGenerateSchema(GenerateSchemaEventArgs $event): void
3741
{
3842
$dbalConnection = $event->getEntityManager()->getConnection();
3943
foreach ($this->transports as $transport) {
@@ -45,8 +49,47 @@ public function postGenerateSchema(GenerateSchemaEventArgs $event)
4549
}
4650
}
4751

52+
public function onSchemaCreateTable(SchemaCreateTableEventArgs $event): void
53+
{
54+
$table = $event->getTable();
55+
56+
// if this method triggers a nested create table below, allow Doctrine to work like normal
57+
if ($table->hasOption(self::PROCESSING_TABLE_FLAG)) {
58+
return;
59+
}
60+
61+
foreach ($this->transports as $transport) {
62+
if (!$transport instanceof DoctrineTransport) {
63+
continue;
64+
}
65+
66+
$extraSql = $transport->getExtraSetupSql($table);
67+
if (null === $extraSql) {
68+
continue;
69+
}
70+
71+
// avoid this same listener from creating a loop on this table
72+
$table->addOption(self::PROCESSING_TABLE_FLAG, true);
73+
$createTableSql = $event->getPlatform()->getCreateTableSQL($table);
74+
75+
/*
76+
* Add all the SQL needed to create the table and tell Doctrine
77+
* to "preventDefault" so that only our SQL is used. This is
78+
* the only way to inject some extra SQL.
79+
*/
80+
$event->addSql($createTableSql);
81+
$event->addSql($extraSql);
82+
$event->preventDefault();
83+
84+
return;
85+
}
86+
}
87+
4888
public function getSubscribedEvents(): array
4989
{
50-
return [ToolEvents::postGenerateSchema];
90+
return [
91+
ToolEvents::postGenerateSchema,
92+
Events::onSchemaCreateTable,
93+
];
5194
}
5295
}

src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/PostgreSqlConnection.php

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\Messenger\Bridge\Doctrine\Transport;
1313

14+
use Doctrine\DBAL\Schema\Table;
15+
1416
/**
1517
* Uses PostgreSQL LISTEN/NOTIFY to push messages to workers.
1618
*
@@ -83,7 +85,25 @@ public function setup(): void
8385
{
8486
parent::setup();
8587

86-
$sql = sprintf(<<<'SQL'
88+
$this->driverConnection->exec($this->getTriggerSql());
89+
}
90+
91+
public function getExtraSetupSql(Table $table): ?string
92+
{
93+
if (!$table->hasOption(self::TABLE_OPTION_NAME)) {
94+
return null;
95+
}
96+
97+
if ($table->getOption(self::TABLE_OPTION_NAME) !== $this->configuration['table_name']) {
98+
return null;
99+
}
100+
101+
return $this->getTriggerSql();
102+
}
103+
104+
private function getTriggerSql(): string
105+
{
106+
return sprintf(<<<'SQL'
87107
LOCK TABLE %1$s;
88108
-- create trigger function
89109
CREATE OR REPLACE FUNCTION notify_%1$s() RETURNS TRIGGER AS $$
@@ -102,7 +122,6 @@ public function setup(): void
102122
FOR EACH ROW EXECUTE PROCEDURE notify_%1$s();
103123
SQL
104124
, $this->configuration['table_name']);
105-
$this->driverConnection->exec($sql);
106125
}
107126

108127
private function unlisten()

0 commit comments

Comments
 (0)
0