mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
Make munging of queries a generic feature
This commit is contained in:
parent
24df564045
commit
f0d30c2eee
14 changed files with 78 additions and 87 deletions
|
@ -1054,9 +1054,9 @@ class Database {
|
||||||
if ($data['read'] || $data['starred'] || strlen($data['note'] ?? "")) {
|
if ($data['read'] || $data['starred'] || strlen($data['note'] ?? "")) {
|
||||||
// first prepare a query to insert any missing marks rows for the articles we want to mark
|
// first prepare a query to insert any missing marks rows for the articles we want to mark
|
||||||
// but only insert new mark records if we're setting at least one "positive" mark
|
// but only insert new mark records if we're setting at least one "positive" mark
|
||||||
$q = $this->articleQuery($user, $context, ["id", "subscription"]);
|
$q = $this->articleQuery($user, $context, ["id", "subscription", "note"]);
|
||||||
$q->setWhere("arsse_marks.starred is null"); // null means there is no marks row for the article
|
$q->setWhere("arsse_marks.starred is null"); // null means there is no marks row for the article
|
||||||
$this->db->prepare("INSERT INTO arsse_marks(article,subscription) ".$q->getQuery(), $q->getTypes())->run($q->getValues());
|
$this->db->prepare("INSERT INTO arsse_marks(article,subscription,note) ".$q->getQuery(), $q->getTypes())->run($q->getValues());
|
||||||
}
|
}
|
||||||
if (isset($data['read']) && (isset($data['starred']) || isset($data['note'])) && ($context->edition() || $context->editions())) {
|
if (isset($data['read']) && (isset($data['starred']) || isset($data['note'])) && ($context->edition() || $context->editions())) {
|
||||||
// if marking by edition both read and something else, do separate marks for starred and note than for read
|
// if marking by edition both read and something else, do separate marks for starred and note than for read
|
||||||
|
|
|
@ -15,6 +15,7 @@ abstract class AbstractStatement implements Statement {
|
||||||
|
|
||||||
abstract public function runArray(array $values = []): Result;
|
abstract public function runArray(array $values = []): Result;
|
||||||
abstract protected function bindValue($value, string $type, int $position): bool;
|
abstract protected function bindValue($value, string $type, int $position): bool;
|
||||||
|
abstract protected function prepare(string $query): bool;
|
||||||
|
|
||||||
public function run(...$values): Result {
|
public function run(...$values): Result {
|
||||||
return $this->runArray($values);
|
return $this->runArray($values);
|
||||||
|
@ -24,6 +25,10 @@ abstract class AbstractStatement implements Statement {
|
||||||
return $this->retypeArray($bindings);
|
return $this->retypeArray($bindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function mungeQuery(string $query, array $types, ...$extraData): string {
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
public function retypeArray(array $bindings, bool $append = false): bool {
|
public function retypeArray(array $bindings, bool $append = false): bool {
|
||||||
if (!$append) {
|
if (!$append) {
|
||||||
$this->types = [];
|
$this->types = [];
|
||||||
|
@ -47,6 +52,9 @@ abstract class AbstractStatement implements Statement {
|
||||||
$this->types[] = self::TYPES[$binding];
|
$this->types[] = self::TYPES[$binding];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!$append) {
|
||||||
|
$this->prepare(static::mungeQuery($this->query, $this->types));
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
21
lib/Db/MySQL/PDOStatement.php
Normal file
21
lib/Db/MySQL/PDOStatement.php
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?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\MySQL;
|
||||||
|
|
||||||
|
class PDOStatement extends \JKingWeb\Arsse\Db\PDOStatement {
|
||||||
|
public static function mungeQuery(string $query, array $types, ...$extraData): string {
|
||||||
|
$query = explode("?", $query);
|
||||||
|
$out = "";
|
||||||
|
for ($b = 1; $b < sizeof($query); $b++) {
|
||||||
|
$a = $b - 1;
|
||||||
|
$mark = (($types[$a] ?? "") == "datetime") ? "cast(? as datetime(0))" : "?";
|
||||||
|
$out .= $query[$a].$mark;
|
||||||
|
}
|
||||||
|
$out .= array_pop($query);
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,12 +30,6 @@ trait PDODriver {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function prepareArray(string $query, array $paramTypes): Statement {
|
public function prepareArray(string $query, array $paramTypes): Statement {
|
||||||
try {
|
return new PDOStatement($this->db, $query, $paramTypes);
|
||||||
$s = $this->db->prepare($query);
|
|
||||||
} catch (\PDOException $e) {
|
|
||||||
list($excClass, $excMsg, $excData) = $this->exceptionBuild();
|
|
||||||
throw new $excClass($excMsg, $excData);
|
|
||||||
}
|
|
||||||
return new PDOStatement($this->db, $s, $paramTypes);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@ namespace JKingWeb\Arsse\Db;
|
||||||
use JKingWeb\Arsse\Db\SQLite3\Driver as SQLite3;
|
use JKingWeb\Arsse\Db\SQLite3\Driver as SQLite3;
|
||||||
|
|
||||||
trait PDOError {
|
trait PDOError {
|
||||||
public function exceptionBuild(bool $statementError = null): array {
|
public function exceptionBuild(bool $statementError = false): array {
|
||||||
if ($statementError ?? ($this instanceof Statement)) {
|
if ($statementError) {
|
||||||
$err = $this->st->errorInfo();
|
$err = $this->st->errorInfo();
|
||||||
} else {
|
} else {
|
||||||
$err = $this->db->errorInfo();
|
$err = $this->db->errorInfo();
|
||||||
|
|
|
@ -20,13 +20,24 @@ class PDOStatement extends AbstractStatement {
|
||||||
|
|
||||||
protected $st;
|
protected $st;
|
||||||
protected $db;
|
protected $db;
|
||||||
|
protected $query;
|
||||||
|
|
||||||
public function __construct(\PDO $db, \PDOStatement $st, array $bindings = []) {
|
public function __construct(\PDO $db, string $query, array $bindings = []) {
|
||||||
$this->db = $db;
|
$this->db = $db;
|
||||||
$this->st = $st;
|
$this->query = $query;
|
||||||
$this->retypeArray($bindings);
|
$this->retypeArray($bindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function prepare(string $query): bool {
|
||||||
|
try {
|
||||||
|
$this->st = $this->db->prepare($query);
|
||||||
|
return true;
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
list($excClass, $excMsg, $excData) = $this->exceptionBuild();
|
||||||
|
throw new $excClass($excMsg, $excData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function __destruct() {
|
public function __destruct() {
|
||||||
unset($this->st, $this->db);
|
unset($this->st, $this->db);
|
||||||
}
|
}
|
||||||
|
@ -37,7 +48,7 @@ class PDOStatement extends AbstractStatement {
|
||||||
try {
|
try {
|
||||||
$this->st->execute();
|
$this->st->execute();
|
||||||
} catch (\PDOException $e) {
|
} catch (\PDOException $e) {
|
||||||
list($excClass, $excMsg, $excData) = $this->exceptionBuild();
|
list($excClass, $excMsg, $excData) = $this->exceptionBuild(true);
|
||||||
throw new $excClass($excMsg, $excData);
|
throw new $excClass($excMsg, $excData);
|
||||||
}
|
}
|
||||||
return new PDOResult($this->db, $this->st);
|
return new PDOResult($this->db, $this->st);
|
||||||
|
|
|
@ -6,51 +6,8 @@
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
namespace JKingWeb\Arsse\Db\PostgreSQL;
|
namespace JKingWeb\Arsse\Db\PostgreSQL;
|
||||||
|
|
||||||
class PDOStatement extends Statement {
|
class PDOStatement extends \JKingWeb\Arsse\Db\PDOStatement {
|
||||||
use \JKingWeb\Arsse\Db\PDOError;
|
public static function mungeQuery(string $query, array $types, ...$extraData): string {
|
||||||
|
return Statement::mungeQuery($query, $types, false);
|
||||||
protected $db;
|
|
||||||
protected $st;
|
|
||||||
protected $qOriginal;
|
|
||||||
protected $qMunged;
|
|
||||||
protected $bindings;
|
|
||||||
|
|
||||||
public function __construct(\PDO $db, string $query, array $bindings = []) {
|
|
||||||
$this->db = $db;
|
|
||||||
$this->qOriginal = $query;
|
|
||||||
$this->retypeArray($bindings);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __destruct() {
|
|
||||||
unset($this->db, $this->st);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function retypeArray(array $bindings, bool $append = false): bool {
|
|
||||||
if ($append) {
|
|
||||||
return parent::retypeArray($bindings, $append);
|
|
||||||
} else {
|
|
||||||
$this->bindings = $bindings;
|
|
||||||
parent::retypeArray($bindings, $append);
|
|
||||||
$this->qMunged = self::mungeQuery($this->qOriginal, $this->types, false);
|
|
||||||
try {
|
|
||||||
// statement creation with PostgreSQL should never fail (it is not evaluated at creation time)
|
|
||||||
$s = $this->db->prepare($this->qMunged);
|
|
||||||
} catch (\PDOException $e) { // @codeCoverageIgnore
|
|
||||||
list($excClass, $excMsg, $excData) = $this->exceptionBuild(true); // @codeCoverageIgnore
|
|
||||||
throw new $excClass($excMsg, $excData); // @codeCoverageIgnore
|
|
||||||
}
|
|
||||||
$this->st = new \JKingWeb\Arsse\Db\PDOStatement($this->db, $s, $this->bindings);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function runArray(array $values = []): \JKingWeb\Arsse\Db\Result {
|
|
||||||
return $this->st->runArray($values);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @codeCoverageIgnore */
|
|
||||||
protected function bindValue($value, string $type, int $position): bool {
|
|
||||||
// stub required by abstract parent, but never used
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,27 +24,16 @@ class Statement extends \JKingWeb\Arsse\Db\AbstractStatement {
|
||||||
|
|
||||||
protected $db;
|
protected $db;
|
||||||
protected $in = [];
|
protected $in = [];
|
||||||
protected $qOriginal;
|
protected $query;
|
||||||
protected $qMunged;
|
protected $qMunged;
|
||||||
protected $bindings;
|
protected $bindings;
|
||||||
|
|
||||||
public function __construct($db, string $query, array $bindings = []) {
|
public function __construct($db, string $query, array $bindings = []) {
|
||||||
$this->db = $db;
|
$this->db = $db;
|
||||||
$this->qOriginal = $query;
|
$this->query = $query;
|
||||||
$this->retypeArray($bindings);
|
$this->retypeArray($bindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function retypeArray(array $bindings, bool $append = false): bool {
|
|
||||||
if ($append) {
|
|
||||||
return parent::retypeArray($bindings, $append);
|
|
||||||
} else {
|
|
||||||
$this->bindings = $bindings;
|
|
||||||
parent::retypeArray($bindings, $append);
|
|
||||||
$this->qMunged = self::mungeQuery($this->qOriginal, $this->types, true);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function runArray(array $values = []): \JKingWeb\Arsse\Db\Result {
|
public function runArray(array $values = []): \JKingWeb\Arsse\Db\Result {
|
||||||
$this->in = [];
|
$this->in = [];
|
||||||
$this->bindValues($values);
|
$this->bindValues($values);
|
||||||
|
@ -62,7 +51,8 @@ class Statement extends \JKingWeb\Arsse\Db\AbstractStatement {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static function mungeQuery(string $q, array $types, bool $mungeParamMarkers = true): string {
|
public static function mungeQuery(string $q, array $types, ...$extraData): string {
|
||||||
|
$mungeParamMarkers = (bool) ($extraData[0] ?? true);
|
||||||
$q = explode("?", $q);
|
$q = explode("?", $q);
|
||||||
$out = "";
|
$out = "";
|
||||||
for ($b = 1; $b < sizeof($q); $b++) {
|
for ($b = 1; $b < sizeof($q); $b++) {
|
||||||
|
@ -74,4 +64,9 @@ class Statement extends \JKingWeb\Arsse\Db\AbstractStatement {
|
||||||
$out .= array_pop($q);
|
$out .= array_pop($q);
|
||||||
return $out;
|
return $out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function prepare(string $query): bool {
|
||||||
|
$this->qMunged = $query;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,13 +166,7 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function prepareArray(string $query, array $paramTypes): \JKingWeb\Arsse\Db\Statement {
|
public function prepareArray(string $query, array $paramTypes): \JKingWeb\Arsse\Db\Statement {
|
||||||
try {
|
return new Statement($this->db, $query, $paramTypes);
|
||||||
$s = $this->db->prepare($query);
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
list($excClass, $excMsg, $excData) = $this->exceptionBuild();
|
|
||||||
throw new $excClass($excMsg, $excData);
|
|
||||||
}
|
|
||||||
return new Statement($this->db, $s, $paramTypes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function lock(): bool {
|
protected function lock(): bool {
|
||||||
|
|
|
@ -27,13 +27,24 @@ class Statement extends \JKingWeb\Arsse\Db\AbstractStatement {
|
||||||
|
|
||||||
protected $db;
|
protected $db;
|
||||||
protected $st;
|
protected $st;
|
||||||
|
protected $query;
|
||||||
|
|
||||||
public function __construct(\SQLite3 $db, \SQLite3Stmt $st, array $bindings = []) {
|
public function __construct(\SQLite3 $db, string $query, array $bindings = []) {
|
||||||
$this->db = $db;
|
$this->db = $db;
|
||||||
$this->st = $st;
|
$this->query = $query;
|
||||||
$this->retypeArray($bindings);
|
$this->retypeArray($bindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function prepare(string $query): bool {
|
||||||
|
try {
|
||||||
|
$this->st = $this->db->prepare($query);
|
||||||
|
return true;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
list($excClass, $excMsg, $excData) = $this->exceptionBuild();
|
||||||
|
throw new $excClass($excMsg, $excData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function __destruct() {
|
public function __destruct() {
|
||||||
try {
|
try {
|
||||||
$this->st->close();
|
$this->st->close();
|
||||||
|
|
|
@ -14,7 +14,7 @@ class TestStatement extends \JKingWeb\Arsse\TestCase\Db\BaseStatement {
|
||||||
protected static $implementation = "PDO MySQL";
|
protected static $implementation = "PDO MySQL";
|
||||||
|
|
||||||
protected function makeStatement(string $q, array $types = []): array {
|
protected function makeStatement(string $q, array $types = []): array {
|
||||||
return [static::$interface, static::$interface->prepare($q), $types];
|
return [static::$interface, $q, $types];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function decorateTypeSyntax(string $value, string $type): string {
|
protected function decorateTypeSyntax(string $value, string $type): string {
|
||||||
|
|
|
@ -19,7 +19,7 @@ class TestStatement extends \JKingWeb\Arsse\TestCase\Db\BaseStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function makeStatement(string $q, array $types = []): array {
|
protected function makeStatement(string $q, array $types = []): array {
|
||||||
return [static::$interface, static::$interface->prepare($q), $types];
|
return [static::$interface, $q, $types];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function decorateTypeSyntax(string $value, string $type): string {
|
protected function decorateTypeSyntax(string $value, string $type): string {
|
||||||
|
|
|
@ -13,7 +13,7 @@ class TestStatement extends \JKingWeb\Arsse\TestCase\Db\BaseStatement {
|
||||||
protected static $implementation = "PDO SQLite 3";
|
protected static $implementation = "PDO SQLite 3";
|
||||||
|
|
||||||
protected function makeStatement(string $q, array $types = []): array {
|
protected function makeStatement(string $q, array $types = []): array {
|
||||||
return [static::$interface, static::$interface->prepare($q), $types];
|
return [static::$interface, $q, $types];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function decorateTypeSyntax(string $value, string $type): string {
|
protected function decorateTypeSyntax(string $value, string $type): string {
|
||||||
|
|
|
@ -290,7 +290,7 @@ class DatabaseInformation {
|
||||||
'PDO MySQL' => [
|
'PDO MySQL' => [
|
||||||
'pdo' => true,
|
'pdo' => true,
|
||||||
'backend' => "MySQL",
|
'backend' => "MySQL",
|
||||||
'statementClass' => \JKingWeb\Arsse\Db\PDOStatement::class,
|
'statementClass' => \JKingWeb\Arsse\Db\MySQL\PDOStatement::class,
|
||||||
'resultClass' => \JKingWeb\Arsse\Db\PDOResult::class,
|
'resultClass' => \JKingWeb\Arsse\Db\PDOResult::class,
|
||||||
'driverClass' => \JKingWeb\Arsse\Db\MySQL\PDODriver::class,
|
'driverClass' => \JKingWeb\Arsse\Db\MySQL\PDODriver::class,
|
||||||
'stringOutput' => true,
|
'stringOutput' => true,
|
||||||
|
|
Loading…
Reference in a new issue