Skip to content

Commit

Permalink
Add support for PostgreSQL
Browse files Browse the repository at this point in the history
Closes #8
  • Loading branch information
theodorejb committed Oct 20, 2024
1 parent e2686a2 commit ea7f7df
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 6 deletions.
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
"scripts": {
"analyze": "psalm",
"test": "phpunit",
"test-mssql": "phpunit --exclude-group mysql",
"test-mysql": "phpunit --exclude-group mssql"
"test-mssql": "phpunit --exclude-group mysql,pgsql",
"test-mysql": "phpunit --exclude-group mssql,pgsql",
"test-pgsql": "phpunit --exclude-group mssql,mysql"
}
}
3 changes: 3 additions & 0 deletions lib/Options.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ class Options
public bool $lastIdIsFirstOfBatch = false;
public bool $fetchNextSyntax = false;
public bool $sqlsrvBinaryEncoding = false;
public bool $binarySelectedAsStream = false;
public bool $nativeBoolColumns = false;
public bool $floatSelectedAsString = false;

/**
* The character used to quote identifiers.
Expand Down
4 changes: 4 additions & 0 deletions lib/PeachySql.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ public function __construct(PDO $connection, ?Options $options = null)
} elseif ($driver === 'mysql') {
$options->lastIdIsFirstOfBatch = true;
$options->identifierQuote = '`'; // needed since not everyone uses ANSI mode
} elseif ($driver === 'pgsql') {
$options->binarySelectedAsStream = true;
$options->nativeBoolColumns = true;
$options->floatSelectedAsString = true;
}
}

Expand Down
48 changes: 44 additions & 4 deletions test/DbTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public function testTransactions(): void

$options = $peachySql->options;
$this->assertSame($options->affectedIsRowCount ? 1 : -1, $result->getAffected());
$expected = ['user_id' => $id, 'is_disabled' => 1];
$expected = ['user_id' => $id, 'is_disabled' => $options->nativeBoolColumns ? true : 1];
$this->assertSame($expected, $result->getFirst()); // the row should be selectable

$peachySql->rollback(); // cancel the transaction
Expand Down Expand Up @@ -126,7 +126,18 @@ public function testIteratorQuery(): void

foreach ($iterator as $row) {
unset($row['user_id']);
$row['is_disabled'] = (bool)$row['is_disabled'];

if ($options->floatSelectedAsString) {
$row['weight'] = (float) $row['weight'];
}
if (!$options->nativeBoolColumns) {
$row['is_disabled'] = (bool) $row['is_disabled'];
}
if ($options->binarySelectedAsStream && $row['uuid'] !== null) {
/** @psalm-suppress MixedArgument */
$row['uuid'] = stream_get_contents($row['uuid']);
}

$colValsCompare[] = $row;
}

Expand Down Expand Up @@ -158,6 +169,13 @@ public function testIteratorQuery(): void
$updatedNames = $result->getAll();
$this->assertSame($options->affectedIsRowCount ? 2 : -1, $result->getAffected());

if ($options->binarySelectedAsStream) {
/** @var array{uuid: resource} $row */
foreach ($updatedNames as &$row) {
$row['uuid'] = stream_get_contents($row['uuid']);
}
}

$this->assertSame($realNames, $updatedNames);
}

Expand Down Expand Up @@ -186,8 +204,9 @@ public function testInsertBulk(): void
$insertColVals[] = $row;
}

$options = $peachySql->options;
$totalBoundParams = count($insertColVals[0]) * $rowCount;
$expectedQueries = ($totalBoundParams > $peachySql->options->maxBoundParams) ? 2 : 1;
$expectedQueries = ($totalBoundParams > $options->maxBoundParams) ? 2 : 1;

$result = $peachySql->insertRows($this->table, $insertColVals);
$this->assertSame($expectedQueries, $result->queryCount);
Expand All @@ -199,6 +218,22 @@ public function testInsertBulk(): void
$rows = $peachySql->selectFrom("SELECT {$columns} FROM {$this->table}")
->where(['user_id' => $ids])->query()->getAll();

if ($options->binarySelectedAsStream || $options->nativeBoolColumns || $options->floatSelectedAsString) {
/** @var array{weight: float|string, is_disabled: int|bool, uuid: string|resource} $row */
foreach ($rows as &$row) {
if (!is_float($row['weight'])) {
$row['weight'] = (float) $row['weight'];
}
if (!is_int($row['is_disabled'])) {
$row['is_disabled'] = (int) $row['is_disabled'];
}
if (!is_string($row['uuid'])) {
/** @psalm-suppress InvalidArgument */
$row['uuid'] = stream_get_contents($row['uuid']);
}
}
}

$this->assertSame($colVals, $rows);

// update the inserted rows
Expand All @@ -210,9 +245,14 @@ public function testInsertBulk(): void
$userId = $ids[0];
$set = ['uuid' => $peachySql->makeBinaryParam($newUuid)];
$peachySql->updateRows($this->table, $set, ['user_id' => $userId]);
/** @var array{uuid: string} $updatedRow */
/** @var array{uuid: string|resource} $updatedRow */
$updatedRow = $peachySql->selectFrom("SELECT uuid FROM {$this->table}")
->where(['user_id' => $userId])->query()->getFirst();

if (!is_string($updatedRow['uuid'])) {
$updatedRow['uuid'] = stream_get_contents($updatedRow['uuid']); // needed for PostgreSQL
}

$this->assertSame($newUuid, $updatedRow['uuid']);

// delete the inserted rows
Expand Down
64 changes: 64 additions & 0 deletions test/Pgsql/PgsqlDbTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

declare(strict_types=1);

namespace PeachySQL\Test\Pgsql;

use PDO;
use PeachySQL\PeachySql;
use PeachySQL\Test\DbTestCase;
use PeachySQL\Test\src\App;

/**
* @group pgsql
*/
class PgsqlDbTest extends DbTestCase
{
private static ?PeachySql $db = null;

protected function getExpectedBadSyntaxCode(): int
{
return 7;
}

protected function getExpectedBadSyntaxError(): string
{
return 'syntax error';
}

protected function getExpectedBadSqlState(): string
{
return '42601';
}

public static function dbProvider(): PeachySql
{
if (!self::$db) {
$c = App::$config;
$pdo = new PDO($c->getPgsqlDsn(), $c->getPgsqlUser(), $c->getPgsqlPassword(), [
PDO::ATTR_EMULATE_PREPARES => false,
]);

self::$db = new PeachySql($pdo);
self::createTestTable(self::$db);
}

return self::$db;
}

private static function createTestTable(PeachySql $db): void
{
$sql = "
CREATE TABLE Users (
user_id SERIAL PRIMARY KEY,
name VARCHAR(50) NOT NULL,
dob DATE NOT NULL,
weight REAL NOT NULL,
is_disabled BOOLEAN NOT NULL,
uuid bytea NULL
)";

$db->query("DROP TABLE IF EXISTS Users");
$db->query($sql);
}
}
15 changes: 15 additions & 0 deletions test/src/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@ public function getMysqlPassword(): string
return '';
}

public function getPgsqlDsn(): string
{
return "pgsql:host=localhost;dbname=PeachySQL";
}

public function getPgsqlUser(): string
{
return 'postgres';
}

public function getPgsqlPassword(): string
{
return '';
}

public function getSqlsrvServer(): string
{
return '(local)\SQLEXPRESS';
Expand Down

0 comments on commit ea7f7df

Please sign in to comment.