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 21, 2024
1 parent ce0bf6a commit facaee6
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 7 deletions.
14 changes: 13 additions & 1 deletion .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ jobs:
ports:
- 3306:3306
options: --health-cmd "mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 5
postgres:
image: postgres
env:
POSTGRES_HOST: localhost
POSTGRES_PASSWORD: postgres
# Set health checks to wait until postgres has started
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
ports:
# Maps tcp port 5432 on service container to the host
- 5432:5432

steps:
- name: Checkout
Expand All @@ -42,4 +52,6 @@ jobs:
if: ${{ matrix.php == '8.3' }}

- name: Run PHPUnit
run: composer test-mysql
run: composer test-without-mssql
env:
POSTGRES_HOST: localhost
6 changes: 4 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@
"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",
"test-without-mssql": "phpunit --exclude-group mssql"
}
}
3 changes: 3 additions & 0 deletions lib/Options.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ class Options
public bool $fetchNextSyntax = false;
public bool $multiRowset = 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 @@ -38,6 +38,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 @@ -75,7 +75,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 @@ -132,7 +132,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 @@ -164,6 +175,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 @@ -192,8 +210,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 @@ -205,6 +224,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 @@ -216,9 +251,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
66 changes: 66 additions & 0 deletions test/Pgsql/PgsqlDbTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?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;
$dbName = getenv('POSTGRES_HOST') !== false ? 'postgres' : 'PeachySQL';

$pdo = new PDO($c->getPgsqlDsn($dbName), $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 $database): string
{
return "pgsql:host=localhost;dbname=$database";
}

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

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

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

0 comments on commit facaee6

Please sign in to comment.