1
1
Fork 0
mirror of https://code.mensbeam.com/MensBeam/Arsse.git synced 2024-12-22 21:22:40 +00:00

Correctly query PostgreSQL byte arrays

This required different workarouynd for the native and PDO interfaces
This commit is contained in:
J. King 2020-11-03 17:52:20 -05:00
parent c21ae3eca9
commit 41bcffd6fb
7 changed files with 94 additions and 3 deletions

View file

@ -0,0 +1,26 @@
<?php
/** @license MIT
* Copyright 2017 J. King, Dustin Wilson et al.
* See LICENSE and AUTHORS files for details */
declare(strict_types=1);
namespace JKingWeb\Arsse\Db\PostgreSQL;
class PDOResult extends \JKingWeb\Arsse\Db\PDOResult {
// This method exists to transparent handle byte-array results
public function valid() {
$this->cur = $this->set->fetch(\PDO::FETCH_ASSOC);
if ($this->cur !== false) {
foreach($this->cur as $k => $v) {
if (is_resource($v)) {
$this->cur[$k] = stream_get_contents($v);
fclose($v);
}
}
return true;
}
return false;
}
}

View file

@ -6,6 +6,8 @@
declare(strict_types=1); declare(strict_types=1);
namespace JKingWeb\Arsse\Db\PostgreSQL; namespace JKingWeb\Arsse\Db\PostgreSQL;
use JKingWeb\Arsse\Db\Result;
class PDOStatement extends \JKingWeb\Arsse\Db\PDOStatement { class PDOStatement extends \JKingWeb\Arsse\Db\PDOStatement {
public static function mungeQuery(string $query, array $types, ...$extraData): string { public static function mungeQuery(string $query, array $types, ...$extraData): string {
return Statement::mungeQuery($query, $types, false); return Statement::mungeQuery($query, $types, false);
@ -16,4 +18,16 @@ class PDOStatement extends \JKingWeb\Arsse\Db\PDOStatement {
// PostgreSQL uses SQLSTATE exclusively, so this is not used // PostgreSQL uses SQLSTATE exclusively, so this is not used
return []; return [];
} }
public function runArray(array $values = []): Result {
$this->st->closeCursor();
$this->bindValues($values);
try {
$this->st->execute();
} catch (\PDOException $e) {
[$excClass, $excMsg, $excData] = $this->buildPDOException(true);
throw new $excClass($excMsg, $excData);
}
return new PDOResult($this->db, $this->st);
}
} }

View file

@ -10,6 +10,7 @@ class Result extends \JKingWeb\Arsse\Db\AbstractResult {
protected $db; protected $db;
protected $r; protected $r;
protected $cur; protected $cur;
protected $blobs = [];
// actual public methods // actual public methods
@ -30,6 +31,11 @@ class Result extends \JKingWeb\Arsse\Db\AbstractResult {
public function __construct($db, $result) { public function __construct($db, $result) {
$this->db = $db; $this->db = $db;
$this->r = $result; $this->r = $result;
for ($a = 0, $stop = pg_num_fields($result); $a < $stop; $a++) {
if (pg_field_type($result, $a) === "bytea") {
$this->blobs[$a] = pg_field_name($result, $a);
}
}
} }
public function __destruct() { public function __destruct() {
@ -41,6 +47,12 @@ class Result extends \JKingWeb\Arsse\Db\AbstractResult {
public function valid() { public function valid() {
$this->cur = pg_fetch_row($this->r, null, \PGSQL_ASSOC); $this->cur = pg_fetch_row($this->r, null, \PGSQL_ASSOC);
return $this->cur !== false; if ($this->cur !== false) {
foreach($this->blobs as $f) {
$this->cur[$f] = hex2bin(substr($this->cur[$f], 2));
}
return true;
}
return false;
} }
} }

View file

@ -10,6 +10,7 @@ use JKingWeb\Arsse\Db\Result;
abstract class BaseResult extends \JKingWeb\Arsse\Test\AbstractTest { abstract class BaseResult extends \JKingWeb\Arsse\Test\AbstractTest {
protected static $insertDefault = "INSERT INTO arsse_test default values"; protected static $insertDefault = "INSERT INTO arsse_test default values";
protected static $selectBlob = "SELECT x'DEADBEEF' as \"blob\"";
protected static $interface; protected static $interface;
protected $resultClass; protected $resultClass;
@ -129,4 +130,16 @@ abstract class BaseResult extends \JKingWeb\Arsse\Test\AbstractTest {
$test = new $this->resultClass(...$this->makeResult("SELECT '2112' as album, '2112' as track union select 'Clockwork Angels' as album, 'The Wreckers' as track")); $test = new $this->resultClass(...$this->makeResult("SELECT '2112' as album, '2112' as track union select 'Clockwork Angels' as album, 'The Wreckers' as track"));
$this->assertEquals($exp, $test->getAll()); $this->assertEquals($exp, $test->getAll());
} }
public function testGetBlobRow(): void {
$exp = ['blob' => hex2bin("DEADBEEF")];
$test = new $this->resultClass(...$this->makeResult(self::$selectBlob));
$this->assertEquals($exp, $test->getRow());
}
public function testGetBlobValue(): void {
$exp = hex2bin("DEADBEEF");
$test = new $this->resultClass(...$this->makeResult(self::$selectBlob));
$this->assertEquals($exp, $test->getValue());
}
} }

View file

@ -15,6 +15,7 @@ class TestResult extends \JKingWeb\Arsse\TestCase\Db\BaseResult {
protected static $createMeta = "CREATE TABLE arsse_meta(key text primary key not null, value text)"; protected static $createMeta = "CREATE TABLE arsse_meta(key text primary key not null, value text)";
protected static $createTest = "CREATE TABLE arsse_test(id bigserial primary key)"; protected static $createTest = "CREATE TABLE arsse_test(id bigserial primary key)";
protected static $selectBlob = "SELECT '\\xDEADBEEF'::bytea as blob";
protected function makeResult(string $q): array { protected function makeResult(string $q): array {
$set = pg_query(static::$interface, $q); $set = pg_query(static::$interface, $q);
@ -29,4 +30,16 @@ class TestResult extends \JKingWeb\Arsse\TestCase\Db\BaseResult {
} }
parent::tearDownAfterClass(); parent::tearDownAfterClass();
} }
public function testGetBlobRow(): void {
$exp = ['blob' => hex2bin("DEADBEEF")];
$test = new $this->resultClass(...$this->makeResult(self::$selectBlob));
$this->assertEquals($exp, $test->getRow());
}
public function testGetBlobValue(): void {
$exp = hex2bin("DEADBEEF");
$test = new $this->resultClass(...$this->makeResult(self::$selectBlob));
$this->assertEquals($exp, $test->getValue());
}
} }

View file

@ -8,16 +8,29 @@ namespace JKingWeb\Arsse\TestCase\Db\PostgreSQLPDO;
/** /**
* @group slow * @group slow
* @covers \JKingWeb\Arsse\Db\PDOResult<extended> * @covers \JKingWeb\Arsse\Db\PostgreSQL\PDOResult<extended>
*/ */
class TestResult extends \JKingWeb\Arsse\TestCase\Db\BaseResult { class TestResult extends \JKingWeb\Arsse\TestCase\Db\BaseResult {
use \JKingWeb\Arsse\Test\DatabaseDrivers\PostgreSQLPDO; use \JKingWeb\Arsse\Test\DatabaseDrivers\PostgreSQLPDO;
protected static $createMeta = "CREATE TABLE arsse_meta(key text primary key not null, value text)"; protected static $createMeta = "CREATE TABLE arsse_meta(key text primary key not null, value text)";
protected static $createTest = "CREATE TABLE arsse_test(id bigserial primary key)"; protected static $createTest = "CREATE TABLE arsse_test(id bigserial primary key)";
protected static $selectBlob = "SELECT '\\xDEADBEEF'::bytea as blob";
protected function makeResult(string $q): array { protected function makeResult(string $q): array {
$set = static::$interface->query($q); $set = static::$interface->query($q);
return [static::$interface, $set]; return [static::$interface, $set];
} }
public function testGetBlobRow(): void {
$exp = ['blob' => hex2bin("DEADBEEF")];
$test = new $this->resultClass(...$this->makeResult(self::$selectBlob));
$this->assertEquals($exp, $test->getRow());
}
public function testGetBlobValue(): void {
$exp = hex2bin("DEADBEEF");
$test = new $this->resultClass(...$this->makeResult(self::$selectBlob));
$this->assertSame($exp, $test->getValue());
}
} }

View file

@ -11,7 +11,7 @@ use JKingWeb\Arsse\Arsse;
trait PostgreSQLPDO { trait PostgreSQLPDO {
protected static $implementation = "PDO PostgreSQL"; protected static $implementation = "PDO PostgreSQL";
protected static $backend = "PostgreSQL"; protected static $backend = "PostgreSQL";
protected static $dbResultClass = \JKingWeb\Arsse\Db\PDOResult::class; protected static $dbResultClass = \JKingWeb\Arsse\Db\PostgreSQL\PDOResult::class;
protected static $dbStatementClass = \JKingWeb\Arsse\Db\PostgreSQL\PDOStatement::class; protected static $dbStatementClass = \JKingWeb\Arsse\Db\PostgreSQL\PDOStatement::class;
protected static $dbDriverClass = \JKingWeb\Arsse\Db\PostgreSQL\PDODriver::class; protected static $dbDriverClass = \JKingWeb\Arsse\Db\PostgreSQL\PDODriver::class;
protected static $stringOutput = false; protected static $stringOutput = false;