Skip to content

Commit

Permalink
Set application_name and character set as DSN string. (#51985)
Browse files Browse the repository at this point in the history
  • Loading branch information
sunaoka committed Jul 3, 2024
1 parent fc70c5e commit 6230d56
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 52 deletions.
50 changes: 11 additions & 39 deletions src/Illuminate/Database/Connectors/PostgresConnector.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,13 @@ public function connect(array $config)

$this->configureIsolationLevel($connection, $config);

$this->configureEncoding($connection, $config);

// Next, we will check to see if a timezone has been specified in this config
// and if it has we will issue a statement to modify the timezone with the
// database. Setting this DB timezone is an optional configuration item.
$this->configureTimezone($connection, $config);

$this->configureSearchPath($connection, $config);

// Postgres allows an application_name to be set by the user and this name is
// used to when monitoring the application with pg_stat_activity. So we'll
// determine if the option has been specified and run a statement if so.
$this->configureApplicationName($connection, $config);

$this->configureSynchronousCommit($connection, $config);

return $connection;
Expand All @@ -71,22 +64,6 @@ protected function configureIsolationLevel($connection, array $config)
}
}

/**
* Set the connection character set and collation.
*
* @param \PDO $connection
* @param array $config
* @return void
*/
protected function configureEncoding($connection, $config)
{
if (! isset($config['charset'])) {
return;
}

$connection->prepare("set names '{$config['charset']}'")->execute();
}

/**
* Set the timezone on the connection.
*
Expand Down Expand Up @@ -132,22 +109,6 @@ protected function quoteSearchPath($searchPath)
return count($searchPath) === 1 ? '"'.$searchPath[0].'"' : '"'.implode('", "', $searchPath).'"';
}

/**
* Set the application name on the connection.
*
* @param \PDO $connection
* @param array $config
* @return void
*/
protected function configureApplicationName($connection, $config)
{
if (isset($config['application_name'])) {
$applicationName = $config['application_name'];

$connection->prepare("set application_name to '$applicationName'")->execute();
}
}

/**
* Create a DSN string from a configuration.
*
Expand Down Expand Up @@ -178,6 +139,17 @@ protected function getDsn(array $config)
$dsn .= ";port={$port}";
}

if (isset($charset)) {
$dsn .= ";client_encoding='{$charset}'";
}

// Postgres allows an application_name to be set by the user and this name is
// used to when monitoring the application with pg_stat_activity. So we'll
// determine if the option has been specified and run a statement if so.
if (isset($application_name)) {
$dsn .= ";application_name='".str_replace("'", "\'", $application_name)."'";
}

return $this->addSslOptions($dsn, $config);
}

Expand Down
23 changes: 10 additions & 13 deletions tests/Database/DatabaseConnectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,15 @@ public function testMySqlConnectCallsCreateConnectionWithIsolationLevel()

public function testPostgresConnectCallsCreateConnectionWithProperArguments()
{
$dsn = 'pgsql:host=foo;dbname=\'bar\';port=111';
$dsn = 'pgsql:host=foo;dbname=\'bar\';port=111;client_encoding=\'utf8\'';
$config = ['host' => 'foo', 'database' => 'bar', 'port' => 111, 'charset' => 'utf8'];
$connector = $this->getMockBuilder(PostgresConnector::class)->onlyMethods(['createConnection', 'getOptions'])->getMock();
$connection = m::mock(stdClass::class);
$connector->expects($this->once())->method('getOptions')->with($this->equalTo($config))->willReturn(['options']);
$connector->expects($this->once())->method('createConnection')->with($this->equalTo($dsn), $this->equalTo($config), $this->equalTo(['options']))->willReturn($connection);
$statement = m::mock(PDOStatement::class);
$connection->shouldReceive('prepare')->once()->with('set names \'utf8\'')->andReturn($statement);
$statement->shouldReceive('execute')->once();
$connection->shouldReceive('prepare')->zeroOrMoreTimes()->andReturn($statement);
$statement->shouldReceive('execute')->zeroOrMoreTimes();
$result = $connector->connect($config);

$this->assertSame($result, $connection);
Expand All @@ -97,16 +97,15 @@ public function testPostgresConnectCallsCreateConnectionWithProperArguments()
*/
public function testPostgresSearchPathIsSet($searchPath, $expectedSql)
{
$dsn = 'pgsql:host=foo;dbname=\'bar\'';
$dsn = 'pgsql:host=foo;dbname=\'bar\';client_encoding=\'utf8\'';
$config = ['host' => 'foo', 'database' => 'bar', 'search_path' => $searchPath, 'charset' => 'utf8'];
$connector = $this->getMockBuilder(PostgresConnector::class)->onlyMethods(['createConnection', 'getOptions'])->getMock();
$connection = m::mock(stdClass::class);
$connector->expects($this->once())->method('getOptions')->with($this->equalTo($config))->willReturn(['options']);
$connector->expects($this->once())->method('createConnection')->with($this->equalTo($dsn), $this->equalTo($config), $this->equalTo(['options']))->willReturn($connection);
$statement = m::mock(PDOStatement::class);
$connection->shouldReceive('prepare')->once()->with('set names \'utf8\'')->andReturn($statement);
$connection->shouldReceive('prepare')->once()->with($expectedSql)->andReturn($statement);
$statement->shouldReceive('execute')->twice();
$statement->shouldReceive('execute')->once();
$result = $connector->connect($config);

$this->assertSame($result, $connection);
Expand Down Expand Up @@ -184,33 +183,31 @@ public static function provideSearchPaths()

public function testPostgresSearchPathFallbackToConfigKeySchema()
{
$dsn = 'pgsql:host=foo;dbname=\'bar\'';
$dsn = 'pgsql:host=foo;dbname=\'bar\';client_encoding=\'utf8\'';
$config = ['host' => 'foo', 'database' => 'bar', 'schema' => ['public', '"user"'], 'charset' => 'utf8'];
$connector = $this->getMockBuilder(PostgresConnector::class)->onlyMethods(['createConnection', 'getOptions'])->getMock();
$connection = m::mock(stdClass::class);
$connector->expects($this->once())->method('getOptions')->with($this->equalTo($config))->willReturn(['options']);
$connector->expects($this->once())->method('createConnection')->with($this->equalTo($dsn), $this->equalTo($config), $this->equalTo(['options']))->willReturn($connection);
$statement = m::mock(PDOStatement::class);
$connection->shouldReceive('prepare')->once()->with('set names \'utf8\'')->andReturn($statement);
$connection->shouldReceive('prepare')->once()->with('set search_path to "public", "user"')->andReturn($statement);
$statement->shouldReceive('execute')->twice();
$statement->shouldReceive('execute')->once();
$result = $connector->connect($config);

$this->assertSame($result, $connection);
}

public function testPostgresApplicationNameIsSet()
{
$dsn = 'pgsql:host=foo;dbname=\'bar\'';
$dsn = 'pgsql:host=foo;dbname=\'bar\';client_encoding=\'utf8\';application_name=\'Laravel App\'';
$config = ['host' => 'foo', 'database' => 'bar', 'charset' => 'utf8', 'application_name' => 'Laravel App'];
$connector = $this->getMockBuilder(PostgresConnector::class)->onlyMethods(['createConnection', 'getOptions'])->getMock();
$connection = m::mock(stdClass::class);
$connector->expects($this->once())->method('getOptions')->with($this->equalTo($config))->willReturn(['options']);
$connector->expects($this->once())->method('createConnection')->with($this->equalTo($dsn), $this->equalTo($config), $this->equalTo(['options']))->willReturn($connection);
$statement = m::mock(PDOStatement::class);
$connection->shouldReceive('prepare')->once()->with('set names \'utf8\'')->andReturn($statement);
$connection->shouldReceive('prepare')->once()->with('set application_name to \'Laravel App\'')->andReturn($statement);
$statement->shouldReceive('execute')->twice();
$connection->shouldReceive('prepare')->zeroOrMoreTimes()->andReturn($statement);
$statement->shouldReceive('execute')->zeroOrMoreTimes();
$result = $connector->connect($config);

$this->assertSame($result, $connection);
Expand Down

0 comments on commit 6230d56

Please sign in to comment.