diff --git a/src/Illuminate/Database/Connectors/PostgresConnector.php b/src/Illuminate/Database/Connectors/PostgresConnector.php index 9bc63269e538..29fe16662f9a 100755 --- a/src/Illuminate/Database/Connectors/PostgresConnector.php +++ b/src/Illuminate/Database/Connectors/PostgresConnector.php @@ -127,7 +127,7 @@ protected function configureSearchPath($connection, $config) protected function parseSearchPath($searchPath) { if (is_string($searchPath)) { - preg_match_all('/[a-zA-z0-9$]{1,}/i', $searchPath, $matches); + preg_match_all('/[^\s,"\']+/', $searchPath, $matches); $searchPath = $matches[0]; } @@ -144,7 +144,7 @@ protected function parseSearchPath($searchPath) /** * Format the search path for the DSN. * - * @param array|string $searchPath + * @param array $searchPath * @return string */ protected function quoteSearchPath($searchPath) diff --git a/tests/Database/DatabaseConnectorTest.php b/tests/Database/DatabaseConnectorTest.php index 938ec0fb7bbf..221223619552 100755 --- a/tests/Database/DatabaseConnectorTest.php +++ b/tests/Database/DatabaseConnectorTest.php @@ -89,27 +89,103 @@ public function testPostgresConnectCallsCreateConnectionWithProperArguments() $this->assertSame($result, $connection); } - public function testPostgresSearchPathIsSet() + /** + * @dataProvider provideSearchPaths + * + * @param string $searchPath + * @param string $expectedSql + */ + public function testPostgresSearchPathIsSet($searchPath, $expectedSql) { $dsn = 'pgsql:host=foo;dbname=\'bar\''; - $config = ['host' => 'foo', 'database' => 'bar', 'search_path' => 'public', 'charset' => '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('set search_path to "public"')->andReturn($statement); + $connection->shouldReceive('prepare')->once()->with($expectedSql)->andReturn($statement); $statement->shouldReceive('execute')->twice(); $result = $connector->connect($config); $this->assertSame($result, $connection); } - public function testPostgresSearchPathArraySupported() + public function provideSearchPaths() + { + return [ + 'all-lowercase' => [ + 'public', + 'set search_path to "public"', + ], + 'case-sensitive' => [ + 'Public', + 'set search_path to "Public"', + ], + 'special characters' => [ + '¡foo_bar-Baz!.Áüõß', + 'set search_path to "¡foo_bar-Baz!.Áüõß"', + ], + 'single-quoted' => [ + "'public'", + 'set search_path to "public"', + ], + 'double-quoted' => [ + '"public"', + 'set search_path to "public"', + ], + 'variable' => [ + '$user', + 'set search_path to "$user"', + ], + 'delimit space' => [ + 'public user', + 'set search_path to "public", "user"', + ], + 'delimit newline' => [ + "public\nuser\r\n\ttest", + 'set search_path to "public", "user", "test"', + ], + 'delimit comma' => [ + 'public,user', + 'set search_path to "public", "user"', + ], + 'delimit comma and space' => [ + 'public, user', + 'set search_path to "public", "user"', + ], + 'single-quoted many' => [ + "'public', 'user'", + 'set search_path to "public", "user"', + ], + 'double-quoted many' => [ + '"public", "user"', + 'set search_path to "public", "user"', + ], + 'quoted space is unsupported in string' => [ + '"public user"', + 'set search_path to "public", "user"', + ], + 'array' => [ + ['public', 'user'], + 'set search_path to "public", "user"', + ], + 'array with variable' => [ + ['public', '$user'], + 'set search_path to "public", "$user"', + ], + 'array with delimiter characters' => [ + ['public', '"user"', "'test'", 'spaced schema'], + 'set search_path to "public", "user", "test", "spaced schema"', + ], + ]; + } + + public function testPostgresSearchPathFallbackToConfigKeySchema() { $dsn = 'pgsql:host=foo;dbname=\'bar\''; - $config = ['host' => 'foo', 'database' => 'bar', 'search_path' => ['public', '"user"'], 'charset' => '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']); @@ -123,38 +199,6 @@ public function testPostgresSearchPathArraySupported() $this->assertSame($result, $connection); } - public function testPostgresSearchPathCommaSeparatedValueSupported() - { - $dsn = 'pgsql:host=foo;dbname=\'bar\''; - $config = ['host' => 'foo', 'database' => 'bar', 'search_path' => 'public, "user"', 'charset' => 'utf8']; - $connector = $this->getMockBuilder('Illuminate\Database\Connectors\PostgresConnector')->setMethods(['createConnection', 'getOptions'])->getMock(); - $connection = m::mock('stdClass'); - $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); - $connection->shouldReceive('prepare')->once()->with('set names \'utf8\'')->andReturn($connection); - $connection->shouldReceive('prepare')->once()->with('set search_path to "public", "user"')->andReturn($connection); - $connection->shouldReceive('execute')->twice(); - $result = $connector->connect($config); - - $this->assertSame($result, $connection); - } - - public function testPostgresSearchPathVariablesSupported() - { - $dsn = 'pgsql:host=foo;dbname=\'bar\''; - $config = ['host' => 'foo', 'database' => 'bar', 'search_path' => '"$user", public, user', 'charset' => 'utf8']; - $connector = $this->getMockBuilder('Illuminate\Database\Connectors\PostgresConnector')->setMethods(['createConnection', 'getOptions'])->getMock(); - $connection = m::mock('stdClass'); - $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); - $connection->shouldReceive('prepare')->once()->with('set names \'utf8\'')->andReturn($connection); - $connection->shouldReceive('prepare')->once()->with('set search_path to "$user", "public", "user"')->andReturn($connection); - $connection->shouldReceive('execute')->twice(); - $result = $connector->connect($config); - - $this->assertSame($result, $connection); - } - public function testPostgresApplicationNameIsSet() { $dsn = 'pgsql:host=foo;dbname=\'bar\'';