Skip to content

Commit 0d1e5dd

Browse files
committed
fix: added validation to IN statement to avoid segfaults
1 parent b42da7f commit 0d1e5dd

11 files changed

Lines changed: 338 additions & 44 deletions

File tree

src/bridge/phpunit/postgresql/src/Flow/Bridge/PHPUnit/PostgreSQL/StaticClient.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ public static function beginTransaction() : void
2626
self::$transactionActive = true;
2727

2828
foreach (self::$clients as $client) {
29+
if (!$client->isConnected()) {
30+
continue;
31+
}
32+
2933
if ($client->getTransactionNestingLevel() === 0) {
3034
$client->beginTransaction();
3135
}
@@ -55,7 +59,7 @@ public static function connect(ConnectionParameters $params, ?ValueConverters $c
5559

5660
$key = \sha1(\sprintf('%s:%d:%s:%s', $params->host(), $params->port(), $params->database(), $params->user() ?? ''));
5761

58-
if (!\array_key_exists($key, self::$clients)) {
62+
if (!\array_key_exists($key, self::$clients) || !self::$clients[$key]->isConnected()) {
5963
self::$clients[$key] = PgSqlClient::connect($params, $converters);
6064

6165
if (self::$transactionActive) {
@@ -93,6 +97,10 @@ public static function rollBack() : void
9397
self::$transactionActive = false;
9498

9599
foreach (self::$clients as $client) {
100+
if (!$client->isConnected()) {
101+
continue;
102+
}
103+
96104
while ($client->getTransactionNestingLevel() > 0) {
97105
$client->rollBack();
98106
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Flow\Bridge\PHPUnit\PostgreSQL\Tests\Context;
6+
7+
use Flow\Bridge\PHPUnit\PostgreSQL\StaticClient;
8+
use Flow\PostgreSql\Client\Client;
9+
10+
final class StaticClientContext
11+
{
12+
public static function injectClient(string $key, Client $client) : void
13+
{
14+
self::injectClients([$key => $client]);
15+
}
16+
17+
/**
18+
* @param array<string, Client> $clients
19+
*/
20+
public static function injectClients(array $clients) : void
21+
{
22+
$reflection = new \ReflectionClass(StaticClient::class);
23+
$property = $reflection->getProperty('clients');
24+
$property->setValue(null, $clients);
25+
}
26+
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Flow\Bridge\PHPUnit\PostgreSQL\Tests\Double;
6+
7+
use Flow\PostgreSql\AST\Transformers\ExplainConfig;
8+
use Flow\PostgreSql\Client\{Client, ConnectionParameters, Cursor, Notification, RowMapper};
9+
use Flow\PostgreSql\Client\Types\ValueConverters;
10+
use Flow\PostgreSql\Explain\Plan\Plan;
11+
use Flow\PostgreSql\QueryBuilder\Sql;
12+
13+
final class FakeClient implements Client
14+
{
15+
public bool $connected = true;
16+
17+
public int $transactionLevel = 0;
18+
19+
public function beginTransaction() : void
20+
{
21+
$this->transactionLevel++;
22+
}
23+
24+
public function close() : void
25+
{
26+
$this->connected = false;
27+
}
28+
29+
public function commit() : void
30+
{
31+
$this->transactionLevel--;
32+
}
33+
34+
public function converters() : ValueConverters
35+
{
36+
throw new \RuntimeException('Not implemented');
37+
}
38+
39+
public function cursor(Sql|string $sql, array $parameters = []) : Cursor
40+
{
41+
throw new \RuntimeException('Not implemented');
42+
}
43+
44+
public function execute(Sql|string $sql, array $parameters = []) : int
45+
{
46+
throw new \RuntimeException('Not implemented');
47+
}
48+
49+
public function explain(Sql|string $sql, array $parameters = [], ?ExplainConfig $config = null) : Plan
50+
{
51+
throw new \RuntimeException('Not implemented');
52+
}
53+
54+
public function fetch(Sql|string $sql, array $parameters = []) : ?array
55+
{
56+
throw new \RuntimeException('Not implemented');
57+
}
58+
59+
public function fetchAll(Sql|string $sql, array $parameters = []) : array
60+
{
61+
throw new \RuntimeException('Not implemented');
62+
}
63+
64+
public function fetchAllInto(RowMapper $mapper, Sql|string $sql, array $parameters = []) : array
65+
{
66+
throw new \RuntimeException('Not implemented');
67+
}
68+
69+
public function fetchInto(RowMapper $mapper, Sql|string $sql, array $parameters = []) : mixed
70+
{
71+
throw new \RuntimeException('Not implemented');
72+
}
73+
74+
public function fetchOne(Sql|string $sql, array $parameters = []) : array
75+
{
76+
throw new \RuntimeException('Not implemented');
77+
}
78+
79+
public function fetchOneInto(RowMapper $mapper, Sql|string $sql, array $parameters = []) : mixed
80+
{
81+
throw new \RuntimeException('Not implemented');
82+
}
83+
84+
public function fetchScalar(Sql|string $sql, array $parameters = []) : mixed
85+
{
86+
throw new \RuntimeException('Not implemented');
87+
}
88+
89+
public function fetchScalarBool(Sql|string $sql, array $parameters = []) : bool
90+
{
91+
throw new \RuntimeException('Not implemented');
92+
}
93+
94+
public function fetchScalarFloat(Sql|string $sql, array $parameters = []) : float
95+
{
96+
throw new \RuntimeException('Not implemented');
97+
}
98+
99+
public function fetchScalarInt(Sql|string $sql, array $parameters = []) : int
100+
{
101+
throw new \RuntimeException('Not implemented');
102+
}
103+
104+
public function fetchScalarString(Sql|string $sql, array $parameters = []) : string
105+
{
106+
throw new \RuntimeException('Not implemented');
107+
}
108+
109+
public function getTransactionNestingLevel() : int
110+
{
111+
return $this->transactionLevel;
112+
}
113+
114+
public function isAutoCommit() : bool
115+
{
116+
throw new \RuntimeException('Not implemented');
117+
}
118+
119+
public function isConnected() : bool
120+
{
121+
return $this->connected;
122+
}
123+
124+
public function lastInsertId(string $sequenceName) : int|string
125+
{
126+
throw new \RuntimeException('Not implemented');
127+
}
128+
129+
public function listen(string $channel) : void
130+
{
131+
throw new \RuntimeException('Not implemented');
132+
}
133+
134+
public function parameters() : ConnectionParameters
135+
{
136+
throw new \RuntimeException('Not implemented');
137+
}
138+
139+
public function rollBack() : void
140+
{
141+
$this->transactionLevel--;
142+
}
143+
144+
public function setAutoCommit(bool $autoCommit) : void
145+
{
146+
throw new \RuntimeException('Not implemented');
147+
}
148+
149+
public function transaction(callable $callback) : mixed
150+
{
151+
throw new \RuntimeException('Not implemented');
152+
}
153+
154+
public function unlisten(string $channel) : void
155+
{
156+
throw new \RuntimeException('Not implemented');
157+
}
158+
159+
public function wait(int $milliseconds) : ?Notification
160+
{
161+
throw new \RuntimeException('Not implemented');
162+
}
163+
}

src/bridge/phpunit/postgresql/tests/Flow/Bridge/PHPUnit/PostgreSQL/Tests/Unit/StaticClientTest.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
namespace Flow\Bridge\PHPUnit\PostgreSQL\Tests\Unit;
66

77
use Flow\Bridge\PHPUnit\PostgreSQL\{SkipTransactionRollback, StaticClient};
8+
use Flow\Bridge\PHPUnit\PostgreSQL\Tests\Context\StaticClientContext;
9+
use Flow\Bridge\PHPUnit\PostgreSQL\Tests\Double\FakeClient;
810
use PHPUnit\Framework\TestCase;
911

1012
#[SkipTransactionRollback]
@@ -20,6 +22,29 @@ protected function tearDown() : void
2022
StaticClient::reset();
2123
}
2224

25+
public function test_begin_transaction_skips_closed_clients() : void
26+
{
27+
$fakeClient = new FakeClient();
28+
$fakeClient->connected = false;
29+
30+
StaticClientContext::injectClient('test-key', $fakeClient);
31+
32+
StaticClient::beginTransaction();
33+
34+
self::assertSame(0, $fakeClient->transactionLevel);
35+
}
36+
37+
public function test_begin_transaction_works_on_connected_clients() : void
38+
{
39+
$fakeClient = new FakeClient();
40+
41+
StaticClientContext::injectClient('test-key', $fakeClient);
42+
43+
StaticClient::beginTransaction();
44+
45+
self::assertSame(1, $fakeClient->transactionLevel);
46+
}
47+
2348
public function test_disable_sets_disabled_state() : void
2449
{
2550
StaticClient::enable();
@@ -49,4 +74,28 @@ public function test_reset_clears_all_state() : void
4974

5075
self::assertFalse(StaticClient::isEnabled());
5176
}
77+
78+
public function test_rollback_skips_closed_clients() : void
79+
{
80+
$fakeClient = new FakeClient();
81+
$fakeClient->connected = false;
82+
83+
StaticClientContext::injectClient('test-key', $fakeClient);
84+
85+
StaticClient::rollBack();
86+
87+
self::assertSame(0, $fakeClient->transactionLevel);
88+
}
89+
90+
public function test_rollback_works_on_connected_clients() : void
91+
{
92+
$fakeClient = new FakeClient();
93+
$fakeClient->transactionLevel = 1;
94+
95+
StaticClientContext::injectClient('test-key', $fakeClient);
96+
97+
StaticClient::rollBack();
98+
99+
self::assertSame(0, $fakeClient->transactionLevel);
100+
}
52101
}

src/bridge/symfony/filesystem-bundle/tests/Flow/Bridge/Symfony/FilesystemBundle/Tests/Unit/FlowFilesystemBundleTest.php

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,11 @@ public function test_build_registers_as_filesystem_factory_attribute_for_autocon
1818

1919
$childDefinition = new ChildDefinition('parent');
2020

21-
if (\method_exists($container, 'getAttributeAutoconfigurators')) {
22-
$autoconfigured = $container->getAttributeAutoconfigurators();
23-
self::assertArrayHasKey(AsFilesystemFactory::class, $autoconfigured);
21+
$autoconfigured = $container->getAttributeAutoconfigurators();
22+
self::assertArrayHasKey(AsFilesystemFactory::class, $autoconfigured);
2423

25-
foreach ($autoconfigured[AsFilesystemFactory::class] as $configurator) {
26-
$configurator($childDefinition, new AsFilesystemFactory(protocol: 'my-fs'), new \ReflectionClass(\stdClass::class));
27-
}
28-
} else {
29-
$autoconfigured = $container->getAutoconfiguredAttributes();
30-
self::assertArrayHasKey(AsFilesystemFactory::class, $autoconfigured);
31-
$autoconfigured[AsFilesystemFactory::class]($childDefinition, new AsFilesystemFactory(protocol: 'my-fs'), new \ReflectionClass(\stdClass::class));
24+
foreach ($autoconfigured[AsFilesystemFactory::class] as $configurator) {
25+
$configurator($childDefinition, new AsFilesystemFactory(protocol: 'my-fs'), new \ReflectionClass(\stdClass::class));
3226
}
3327

3428
$tags = $childDefinition->getTag('flow_filesystem.factory');

0 commit comments

Comments
 (0)