Browse Source
PDO does not adequately inform PostgreSQL of a parameter's type, so type casts are required. Rather than adding these to each query manually, the queries are instead processed to add type hints automatically. Unfortunately the queries are processed rather naively; question-mark characters in string constants, identifiers, regex patterns, or geometry operators will break things spectacularly.microsub
J. King
5 years ago
9 changed files with 97 additions and 15 deletions
@ -0,0 +1,77 @@ |
|||
<?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 PDOStatement extends \JKingWeb\Arsse\Db\AbstractStatement { |
|||
use \JKingWeb\Arsse\Db\PDOError; |
|||
|
|||
const BINDINGS = [ |
|||
"integer" => "bigint", |
|||
"float" => "decimal", |
|||
"datetime" => "timestamp", |
|||
"binary" => "bytea", |
|||
"string" => "text", |
|||
"boolean" => "smallint", // FIXME: using boolean leads to incompatibilities with versions of SQLite bundled prior to PHP 7.3 |
|||
]; |
|||
|
|||
protected $db; |
|||
protected $st; |
|||
protected $qOriginal; |
|||
protected $qMunged; |
|||
protected $bindings; |
|||
|
|||
public function __construct(\PDO $db, string $query, array $bindings = []) { |
|||
$this->db = $db; // both db and st are the same object due to the logic of the PDOError handler |
|||
$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 { |
|||
$s = $this->db->prepare($this->qMunged); |
|||
$this->st = new \JKingWeb\Arsse\Db\PDOStatement($this->db, $s, $this->bindings); |
|||
} catch (\PDOException $e) { |
|||
list($excClass, $excMsg, $excData) = $this->exceptionBuild(true); |
|||
throw new $excClass($excMsg, $excData); |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
public static function mungeQuery(string $q, array $types, bool $mungeParamMarkers = true): string { |
|||
$q = explode("?", $q); |
|||
$out = ""; |
|||
for ($b = 1; $b < sizeof($q); $b++) { |
|||
$a = $b - 1; |
|||
$mark = $mungeParamMarkers ? "\$$b" : "?"; |
|||
$type = isset($types[$a]) ? "::".self::BINDINGS[$types[$a]] : ""; |
|||
$out .= $q[$a].$mark.$type; |
|||
} |
|||
$out .= array_pop($q); |
|||
return $out; |
|||
} |
|||
|
|||
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 $value; |
|||
} |
|||
} |
Loading…
Reference in new issue