mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
Merge branch 'master' into miniflux
This commit is contained in:
commit
5e60da00a9
11 changed files with 83 additions and 10 deletions
26
lib/Db/PostgreSQL/PDOResult.php
Normal file
26
lib/Db/PostgreSQL/PDOResult.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,9 @@ class Statement extends \JKingWeb\Arsse\Db\AbstractStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function bindValue($value, int $type, int $position): bool {
|
protected function bindValue($value, int $type, int $position): bool {
|
||||||
|
if ($value !== null && ($this->types[$position - 1] % self::T_NOT_NULL) === self::T_BINARY) {
|
||||||
|
$value = "\\x".bin2hex($value);
|
||||||
|
}
|
||||||
$this->in[] = $value;
|
$this->in[] = $value;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(static::$selectBlob));
|
||||||
|
$this->assertEquals($exp, $test->getRow());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetBlobValue(): void {
|
||||||
|
$exp = hex2bin("DEADBEEF");
|
||||||
|
$test = new $this->resultClass(...$this->makeResult(static::$selectBlob));
|
||||||
|
$this->assertEquals($exp, $test->getValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,6 @@ abstract class BaseStatement extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
} else {
|
} else {
|
||||||
$query = "SELECT ($exp = ?) as pass";
|
$query = "SELECT ($exp = ?) as pass";
|
||||||
}
|
}
|
||||||
$typeStr = "'".str_replace("'", "''", $type)."'";
|
|
||||||
$s = new $this->statementClass(...$this->makeStatement($query));
|
$s = new $this->statementClass(...$this->makeStatement($query));
|
||||||
$s->retype(...[$type]);
|
$s->retype(...[$type]);
|
||||||
$act = $s->run(...[$value])->getValue();
|
$act = $s->run(...[$value])->getValue();
|
||||||
|
@ -66,15 +65,11 @@ abstract class BaseStatement extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
|
|
||||||
/** @dataProvider provideBinaryBindings */
|
/** @dataProvider provideBinaryBindings */
|
||||||
public function testHandleBinaryData($value, string $type, string $exp): void {
|
public function testHandleBinaryData($value, string $type, string $exp): void {
|
||||||
if (in_array(static::$implementation, ["PostgreSQL", "PDO PostgreSQL"])) {
|
|
||||||
$this->markTestIncomplete("Correct handling of binary data with PostgreSQL is not currently implemented");
|
|
||||||
}
|
|
||||||
if ($exp === "null") {
|
if ($exp === "null") {
|
||||||
$query = "SELECT (? is null) as pass";
|
$query = "SELECT (? is null) as pass";
|
||||||
} else {
|
} else {
|
||||||
$query = "SELECT ($exp = ?) as pass";
|
$query = "SELECT ($exp = ?) as pass";
|
||||||
}
|
}
|
||||||
$typeStr = "'".str_replace("'", "''", $type)."'";
|
|
||||||
$s = new $this->statementClass(...$this->makeStatement($query));
|
$s = new $this->statementClass(...$this->makeStatement($query));
|
||||||
$s->retype(...[$type]);
|
$s->retype(...[$type]);
|
||||||
$act = $s->run(...[$value])->getValue();
|
$act = $s->run(...[$value])->getValue();
|
||||||
|
@ -297,13 +292,11 @@ abstract class BaseStatement extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
'UTF-8 string as strict binary' => ["\u{e9}", "strict binary", "x'c3a9'"],
|
'UTF-8 string as strict binary' => ["\u{e9}", "strict binary", "x'c3a9'"],
|
||||||
'Binary string as integer' => [chr(233).chr(233), "integer", "0"],
|
'Binary string as integer' => [chr(233).chr(233), "integer", "0"],
|
||||||
'Binary string as float' => [chr(233).chr(233), "float", "0.0"],
|
'Binary string as float' => [chr(233).chr(233), "float", "0.0"],
|
||||||
'Binary string as string' => [chr(233).chr(233), "string", "'".chr(233).chr(233)."'"],
|
|
||||||
'Binary string as binary' => [chr(233).chr(233), "binary", "x'e9e9'"],
|
'Binary string as binary' => [chr(233).chr(233), "binary", "x'e9e9'"],
|
||||||
'Binary string as datetime' => [chr(233).chr(233), "datetime", "null"],
|
'Binary string as datetime' => [chr(233).chr(233), "datetime", "null"],
|
||||||
'Binary string as boolean' => [chr(233).chr(233), "boolean", "1"],
|
'Binary string as boolean' => [chr(233).chr(233), "boolean", "1"],
|
||||||
'Binary string as strict integer' => [chr(233).chr(233), "strict integer", "0"],
|
'Binary string as strict integer' => [chr(233).chr(233), "strict integer", "0"],
|
||||||
'Binary string as strict float' => [chr(233).chr(233), "strict float", "0.0"],
|
'Binary string as strict float' => [chr(233).chr(233), "strict float", "0.0"],
|
||||||
'Binary string as strict string' => [chr(233).chr(233), "strict string", "'".chr(233).chr(233)."'"],
|
|
||||||
'Binary string as strict binary' => [chr(233).chr(233), "strict binary", "x'e9e9'"],
|
'Binary string as strict binary' => [chr(233).chr(233), "strict binary", "x'e9e9'"],
|
||||||
'Binary string as strict datetime' => [chr(233).chr(233), "strict datetime", "'0001-01-01 00:00:00'"],
|
'Binary string as strict datetime' => [chr(233).chr(233), "strict datetime", "'0001-01-01 00:00:00'"],
|
||||||
'Binary string as strict boolean' => [chr(233).chr(233), "strict boolean", "1"],
|
'Binary string as strict boolean' => [chr(233).chr(233), "strict boolean", "1"],
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -27,6 +27,11 @@ class TestStatement extends \JKingWeb\Arsse\TestCase\Db\BaseStatement {
|
||||||
return "U&'\\+".str_pad(dechex((int) $match[1]), 6, "0", \STR_PAD_LEFT)."'";
|
return "U&'\\+".str_pad(dechex((int) $match[1]), 6, "0", \STR_PAD_LEFT)."'";
|
||||||
}
|
}
|
||||||
return $value;
|
return $value;
|
||||||
|
case "binary":
|
||||||
|
if ($value[0] === "x") {
|
||||||
|
return "'\\x".substr($value, 2)."::bytea";
|
||||||
|
}
|
||||||
|
// no break;
|
||||||
default:
|
default:
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,13 +8,14 @@ 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);
|
||||||
|
|
|
@ -27,6 +27,11 @@ class TestStatement extends \JKingWeb\Arsse\TestCase\Db\BaseStatement {
|
||||||
return "U&'\\+".str_pad(dechex((int) $match[1]), 6, "0", \STR_PAD_LEFT)."'";
|
return "U&'\\+".str_pad(dechex((int) $match[1]), 6, "0", \STR_PAD_LEFT)."'";
|
||||||
}
|
}
|
||||||
return $value;
|
return $value;
|
||||||
|
case "binary":
|
||||||
|
if ($value[0] === "x") {
|
||||||
|
return "'\\x".substr($value, 2)."::bytea";
|
||||||
|
}
|
||||||
|
// no break;
|
||||||
default:
|
default:
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue