Skip to content

Commit 99b93bf

Browse files
authored
fix: [Postgre] Semicolon in the connection parameters break the DSN string (#7552)
move all related code to the convertDSN() method
1 parent 0798239 commit 99b93bf

3 files changed

Lines changed: 85 additions & 5 deletions

File tree

system/Database/Postgre/Connection.php

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,11 @@ public function connect(bool $persistent = false)
6464
$this->buildDSN();
6565
}
6666

67-
// Strip pgsql if exists
67+
// Convert DSN string
6868
if (mb_strpos($this->DSN, 'pgsql:') === 0) {
69-
$this->DSN = mb_substr($this->DSN, 6);
69+
$this->convertDSN();
7070
}
7171

72-
// Convert semicolons to spaces.
73-
$this->DSN = str_replace(';', ' ', $this->DSN);
74-
7572
$this->connID = $persistent === true ? pg_pconnect($this->DSN) : pg_connect($this->DSN);
7673

7774
if ($this->connID !== false) {
@@ -92,6 +89,44 @@ public function connect(bool $persistent = false)
9289
return $this->connID;
9390
}
9491

92+
/**
93+
* Converts the DSN with semicolon syntax.
94+
*/
95+
private function convertDSN()
96+
{
97+
// Strip pgsql
98+
$this->DSN = mb_substr($this->DSN, 6);
99+
100+
// Convert semicolons to spaces in DSN format like:
101+
// pgsql:host=localhost;port=5432;dbname=database_name
102+
// https://www.php.net/manual/en/function.pg-connect.php
103+
$allowedParams = ['host', 'port', 'dbname', 'user', 'password', 'connect_timeout', 'options', 'sslmode', 'service'];
104+
105+
$parameters = explode(';', $this->DSN);
106+
107+
$output = '';
108+
$previousParameter = '';
109+
110+
foreach ($parameters as $parameter) {
111+
[$key, $value] = explode('=', $parameter, 2);
112+
if (in_array($key, $allowedParams, true)) {
113+
if ($previousParameter !== '') {
114+
if (array_search($key, $allowedParams, true) < array_search($previousParameter, $allowedParams, true)) {
115+
$output .= ';';
116+
} else {
117+
$output .= ' ';
118+
}
119+
}
120+
$output .= $parameter;
121+
$previousParameter = $key;
122+
} else {
123+
$output .= ';' . $parameter;
124+
}
125+
}
126+
127+
$this->DSN = $output;
128+
}
129+
95130
/**
96131
* Keep or establish the connection if no queries have been sent for
97132
* a length of time exceeding the server's idle timeout.

tests/system/Database/ConfigTest.php

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

1414
use CodeIgniter\Test\CIUnitTestCase;
1515
use CodeIgniter\Test\ReflectionHelper;
16+
use Generator;
1617

1718
/**
1819
* @internal
@@ -190,4 +191,47 @@ public function testConnectionGroupWithDSNPostgreNative()
190191
$this->assertTrue($this->getPrivateProperty($conn, 'strictOn'));
191192
$this->assertSame([], $this->getPrivateProperty($conn, 'failover'));
192193
}
194+
195+
/**
196+
* @dataProvider convertDSNProvider
197+
*
198+
* @see https://github.com/codeigniter4/CodeIgniter4/issues/7550
199+
*/
200+
public function testConvertDSN(string $input, string $expected)
201+
{
202+
$this->dsnGroupPostgreNative['DSN'] = $input;
203+
$conn = Config::connect($this->dsnGroupPostgreNative, false);
204+
$this->assertInstanceOf(BaseConnection::class, $conn);
205+
206+
$method = $this->getPrivateMethodInvoker($conn, 'convertDSN');
207+
$method();
208+
209+
$this->assertSame($expected, $this->getPrivateProperty($conn, 'DSN'));
210+
}
211+
212+
public function convertDSNProvider(): Generator
213+
{
214+
yield from [
215+
[
216+
'pgsql:host=localhost;port=5432;dbname=database_name;user=username;password=password',
217+
'host=localhost port=5432 dbname=database_name user=username password=password',
218+
],
219+
[
220+
'pgsql:host=localhost;port=5432;dbname=database_name;user=username;password=we;port=we',
221+
'host=localhost port=5432 dbname=database_name user=username password=we;port=we',
222+
],
223+
[
224+
'pgsql:host=localhost;port=5432;dbname=database_name',
225+
'host=localhost port=5432 dbname=database_name',
226+
],
227+
[
228+
"pgsql:host=localhost;port=5432;dbname=database_name;options='--client_encoding=UTF8'",
229+
"host=localhost port=5432 dbname=database_name options='--client_encoding=UTF8'",
230+
],
231+
[
232+
'pgsql:host=localhost;port=5432;dbname=database_name;something=stupid',
233+
'host=localhost port=5432 dbname=database_name;something=stupid',
234+
],
235+
];
236+
}
193237
}

user_guide_src/source/changelogs/v4.3.6.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ Bugs Fixed
5050
the value of a placeholder.
5151
- **Validation:** Fixed a bug that ``check()`` cannot specify non-default
5252
database group.
53+
- **Database:** Fixed a bug where semicolon character (``;``) in one of the Postgre connection parameters would break the DSN string.
5354

5455
See the repo's
5556
`CHANGELOG.md <https://github.com/codeigniter4/CodeIgniter4/blob/develop/CHANGELOG.md>`_

0 commit comments

Comments
 (0)