-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
440 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
<?php | ||
|
||
namespace duncan3dc\Sql\Driver\Postgres; | ||
|
||
use duncan3dc\Sql\Driver\ResultInterface; | ||
|
||
class Result implements ResultInterface | ||
{ | ||
|
||
/** | ||
* Create a new instance. | ||
* | ||
* @param mixed $result Something returned by pg_query_params | ||
*/ | ||
public function __construct($result) | ||
{ | ||
$this->result = $result; | ||
} | ||
|
||
|
||
/** | ||
* Fetch the next row from the result set. | ||
* | ||
* @return array|null | ||
*/ | ||
public function getNextRow() | ||
{ | ||
$row = pg_fetch_assoc($this->result); | ||
|
||
if (is_array($row)) { | ||
return $row; | ||
} | ||
|
||
return null; | ||
} | ||
|
||
|
||
/** | ||
* Free the memory used by the result resource. | ||
* | ||
* @return void | ||
*/ | ||
public function free() | ||
{ | ||
if (is_resource($this->result)) { | ||
pg_free_result($this->result); | ||
$this->result = null; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
<?php | ||
|
||
namespace duncan3dc\Sql\Driver\Postgres; | ||
|
||
use duncan3dc\PhpIni\State; | ||
use duncan3dc\Sql\Driver\ServerInterface; | ||
|
||
class Server implements ServerInterface | ||
{ | ||
/** | ||
* @var resource $pg The connection to the database server. | ||
*/ | ||
private $pg; | ||
|
||
/** | ||
* @var string $database The database to use. | ||
*/ | ||
private $database; | ||
|
||
/** | ||
* @var string $hostname The host or ip address of the database server. | ||
*/ | ||
private $hostname; | ||
|
||
/** | ||
* @var string $username The user to authenticate with. | ||
*/ | ||
private $username; | ||
|
||
/** | ||
* @var string $password The password to authenticate with. | ||
*/ | ||
private $password; | ||
|
||
/** | ||
* @var int $port The port to connect on. | ||
*/ | ||
private $port; | ||
|
||
/** | ||
* @var State $ini An ini state object to supress warnings. | ||
*/ | ||
private $ini; | ||
|
||
|
||
/** | ||
* Create a new instance. | ||
* | ||
* @param string $database The database to use | ||
* @param string $hostname The host or ip address of the database server | ||
* @param string $username The user to authenticate with | ||
* @param string $password The password to authenticate with | ||
*/ | ||
public function __construct(string $database, string $hostname, string $username, string $password) | ||
{ | ||
$this->database = $database; | ||
$this->hostname = $hostname; | ||
$this->username = $username; | ||
$this->password = $password; | ||
|
||
$this->ini = new State; | ||
$this->ini->set("error_reporting", error_reporting() ^ \E_WARNING); | ||
} | ||
|
||
|
||
/** | ||
* Set the port to connect on. | ||
* | ||
* @param int $port The port number | ||
* | ||
* @return $this | ||
*/ | ||
public function setPort(int $port): ServerInterface | ||
{ | ||
$this->port = $port; | ||
} | ||
|
||
|
||
/** | ||
* Connect to the database using the previously supplied credentials. | ||
* | ||
* @return bool | ||
*/ | ||
public function connect(): bool | ||
{ | ||
$options = [ | ||
"host" => $this->hostname, | ||
"user" => $this->username, | ||
"password" => $this->password, | ||
"dbname" => $this->database, | ||
"port" => $this->port, | ||
]; | ||
|
||
$connect = ""; | ||
foreach ($options as $key => $val) { | ||
if ($val === null) { | ||
continue; | ||
} | ||
$connect .= "{$key}={$val} "; | ||
} | ||
|
||
$this->ini->call(function () use ($connect) { | ||
$this->pg = pg_connect($connect, \PGSQL_CONNECT_FORCE_NEW); | ||
}); | ||
|
||
if (!$this->pg) { | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
|
||
/** | ||
* Run a query. | ||
* | ||
* @param string $query The query to run | ||
* @param array $params The parameters to substitute in the query string | ||
* @param string $preparedQuery A simulated prepared query (if the server doesn't support prepared statements) | ||
* | ||
* @return ResultInterface|null Successful statements should return a Result instance | ||
*/ | ||
public function query(string $query, array $params, string $preparedQuery) | ||
{ | ||
$tmpQuery = $query; | ||
$query = ""; | ||
|
||
$i = 1; | ||
while ($pos = strpos($tmpQuery, "?")) { | ||
$query .= substr($tmpQuery, 0, $pos) . "\$" . $i++; | ||
$tmpQuery = substr($tmpQuery, $pos + 1); | ||
} | ||
$query .= $tmpQuery; | ||
|
||
$result = $this->ini->call(function () use ($query, $params) { | ||
return pg_query_params($this->pg, $query, $params); | ||
}); | ||
|
||
if ($result) { | ||
return new Result($result); | ||
} | ||
} | ||
|
||
|
||
/** | ||
* Quote the supplied string with the relevant characters used by the database driver. | ||
* | ||
* @param string $value The string to quote | ||
* | ||
* @return string The quoted string | ||
*/ | ||
public function quoteValue(string $value): string | ||
{ | ||
return pg_escape_literal($this->pg, $value); | ||
} | ||
|
||
|
||
/** | ||
* Get the error code of the last error. | ||
* | ||
* @return mixed | ||
*/ | ||
public function getErrorCode() | ||
{ | ||
return 0; | ||
} | ||
|
||
|
||
/** | ||
* Get the error message text of the last error. | ||
* | ||
* @return string | ||
*/ | ||
public function getErrorMessage(): string | ||
{ | ||
if (!$this->pg) { | ||
$error = error_get_last(); | ||
if (array_key_exists("message", $error)) { | ||
return explode("\n", $error["message"])[0]; | ||
} | ||
} | ||
|
||
return pg_last_error($this->pg); | ||
} | ||
|
||
|
||
/** | ||
* Close the sql connection. | ||
* | ||
* @return bool | ||
*/ | ||
public function disconnect(): bool | ||
{ | ||
if (!$this->pg) { | ||
return true; | ||
} | ||
|
||
$result = pg_close($this->pg); | ||
|
||
$this->pg = null; | ||
|
||
return $result; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<?php | ||
|
||
namespace duncan3dc\SqlTests\Driver\Postgres; | ||
|
||
use duncan3dc\Sql\Driver\Postgres\Server; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
abstract class AbstractTest extends TestCase | ||
{ | ||
private static $server; | ||
|
||
public static function setUpBeforeClass() | ||
{ | ||
if (!isset($_ENV["POSTGRES_PASSWORD"])) { | ||
$_ENV["POSTGRES_PASSWORD"] = "pass"; | ||
} | ||
|
||
self::$server = new Server("postgres", "127.0.0.1", "postgres", $_ENV["POSTGRES_PASSWORD"]); | ||
self::$server->connect(); | ||
self::$server->query("CREATE DATABASE pgdb", [], ""); | ||
} | ||
|
||
|
||
public static function tearDownAfterClass() | ||
{ | ||
self::$server->query("DROP DATABASE pgdb", [], ""); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
<?php | ||
|
||
namespace duncan3dc\SqlTests\Driver\Postgres; | ||
|
||
use duncan3dc\Sql\Driver\Postgres\Result; | ||
use duncan3dc\Sql\Driver\Postgres\Server; | ||
|
||
class ResultTest extends AbstractTest | ||
{ | ||
private $server; | ||
|
||
|
||
public function setUp() | ||
{ | ||
$this->server = new Server("pgdb", "127.0.0.1", "postgres", $_ENV["POSTGRES_PASSWORD"]); | ||
$this->server->connect(); | ||
} | ||
|
||
|
||
public function tearDown() | ||
{ | ||
$this->server->disconnect(); | ||
} | ||
|
||
|
||
public function testGetNextRow() | ||
{ | ||
$this->server->query("CREATE TEMPORARY TABLE bands (name VARCHAR(20), year INTEGER)", [], ""); | ||
$this->server->query("INSERT INTO bands VALUES ('metallica', 1981), ('protest the hero', 2002)", [], ""); | ||
|
||
$result = $this->server->query("SELECT name, year FROM bands", [], ""); | ||
|
||
$this->assertSame([ | ||
"name" => "metallica", | ||
"year" => "1981", | ||
], $result->getNextRow()); | ||
|
||
$this->assertSame([ | ||
"name" => "protest the hero", | ||
"year" => "2002", | ||
], $result->getNextRow()); | ||
|
||
$this->assertNull($result->getNextRow()); | ||
} | ||
|
||
|
||
public function testFree() | ||
{ | ||
$result = $this->server->query("SELECT current_database()", [], ""); | ||
|
||
$result->free(); | ||
$result->free(); | ||
|
||
$this->assertInstanceOf(Result::class, $result); | ||
} | ||
} |
Oops, something went wrong.