2018-11-10 05:02:38 +00:00
|
|
|
<?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;
|
|
|
|
|
|
|
|
use JKingWeb\Arsse\Arsse;
|
|
|
|
use JKingWeb\Arsse\Conf;
|
|
|
|
use JKingWeb\Arsse\Db\Exception;
|
|
|
|
use JKingWeb\Arsse\Db\ExceptionInput;
|
|
|
|
use JKingWeb\Arsse\Db\ExceptionTimeout;
|
|
|
|
|
|
|
|
class Driver extends \JKingWeb\Arsse\Db\AbstractDriver {
|
2018-11-17 02:20:54 +00:00
|
|
|
public function __construct(string $user = null, string $pass = null, string $db = null, string $host = null, int $port = null, string $schema = null, string $service = null) {
|
2018-11-10 05:02:38 +00:00
|
|
|
// check to make sure required extension is loaded
|
|
|
|
if (!static::requirementsMet()) {
|
2018-11-21 16:06:12 +00:00
|
|
|
throw new Exception("extMissing", static::driverName()); // @codeCoverageIgnore
|
2018-11-10 05:02:38 +00:00
|
|
|
}
|
|
|
|
$user = $user ?? Arsse::$conf->dbPostgreSQLUser;
|
|
|
|
$pass = $pass ?? Arsse::$conf->dbPostgreSQLPass;
|
|
|
|
$db = $db ?? Arsse::$conf->dbPostgreSQLDb;
|
|
|
|
$host = $host ?? Arsse::$conf->dbPostgreSQLHost;
|
|
|
|
$port = $port ?? Arsse::$conf->dbPostgreSQLPort;
|
|
|
|
$schema = $schema ?? Arsse::$conf->dbPostgreSQLSchema;
|
2018-11-17 02:20:54 +00:00
|
|
|
$service = $service ?? Arsse::$conf->dbPostgreSQLService;
|
|
|
|
$this->makeConnection($user, $pass, $db, $host, $port, $service);
|
2018-11-21 16:06:12 +00:00
|
|
|
foreach (static::makeSetupQueries($schema) as $q) {
|
|
|
|
$this->exec($q);
|
|
|
|
}
|
2018-11-10 05:02:38 +00:00
|
|
|
}
|
|
|
|
|
2018-11-17 02:20:54 +00:00
|
|
|
public static function makeConnectionString(bool $pdo, string $user, string $pass, string $db, string $host, int $port, string $service): string {
|
|
|
|
$base = [
|
|
|
|
'client_encoding' => "UTF8",
|
|
|
|
'application_name' => "arsse",
|
2018-11-22 18:30:13 +00:00
|
|
|
'connect_timeout' => (string) ceil(Arsse::$conf->dbTimeoutConnect ?? 0),
|
2018-11-17 02:20:54 +00:00
|
|
|
];
|
|
|
|
$out = [];
|
|
|
|
if ($service != "") {
|
|
|
|
$out['service'] = $service;
|
|
|
|
} else {
|
|
|
|
if ($host != "") {
|
|
|
|
$out['host'] = $host;
|
|
|
|
}
|
|
|
|
if ($port != 5432 && !($host != "" && $host[0] == "/")) {
|
|
|
|
$out['port'] = (string) $port;
|
|
|
|
}
|
|
|
|
if ($db != "") {
|
|
|
|
$out['dbname'] = $db;
|
|
|
|
}
|
|
|
|
if (!$pdo) {
|
|
|
|
$out['user'] = $user;
|
|
|
|
if ($pass != "") {
|
|
|
|
$out['password'] = $pass;
|
|
|
|
}
|
|
|
|
}
|
2018-11-10 05:02:38 +00:00
|
|
|
}
|
2018-11-17 02:20:54 +00:00
|
|
|
ksort($out);
|
|
|
|
ksort($base);
|
|
|
|
$out = array_merge($out, $base);
|
2018-11-10 05:02:38 +00:00
|
|
|
$out = array_map(function($v, $k) {
|
|
|
|
return "$k='".str_replace("'", "\\'", str_replace("\\", "\\\\", $v))."'";
|
|
|
|
}, $out, array_keys($out));
|
2018-11-17 02:20:54 +00:00
|
|
|
return implode(" ", $out);
|
2018-11-10 05:02:38 +00:00
|
|
|
}
|
|
|
|
|
2018-11-21 16:06:12 +00:00
|
|
|
public static function makeSetupQueries(string $schema = ""): array {
|
2018-11-22 18:30:13 +00:00
|
|
|
$timeout = ceil(Arsse::$conf->dbTimeoutExec * 1000);
|
2018-11-21 16:06:12 +00:00
|
|
|
$out = [
|
|
|
|
"SET TIME ZONE UTC",
|
2018-11-22 18:30:13 +00:00
|
|
|
"SET DateStyle = 'ISO, MDY'",
|
|
|
|
"SET statement_timeout = '$timeout'",
|
2018-11-21 16:06:12 +00:00
|
|
|
];
|
|
|
|
if (strlen($schema) > 0) {
|
|
|
|
$out[] = 'SET search_path = \'"'.str_replace('"', '""', $schema).'", "$user", public\'';
|
|
|
|
}
|
|
|
|
return $out;
|
2018-11-10 05:02:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/** @codeCoverageIgnore */
|
|
|
|
public static function create(): \JKingWeb\Arsse\Db\Driver {
|
|
|
|
if (self::requirementsMet()) {
|
|
|
|
return new self;
|
|
|
|
} elseif (PDODriver::requirementsMet()) {
|
|
|
|
return new PDODriver;
|
|
|
|
} else {
|
|
|
|
throw new Exception("extMissing", self::driverName());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static function driverName(): string {
|
|
|
|
return Arsse::$lang->msg("Driver.Db.PostgreSQL.Name");
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function schemaID(): string {
|
|
|
|
return "PostgreSQL";
|
|
|
|
}
|
|
|
|
|
2018-11-21 16:06:12 +00:00
|
|
|
public function charsetAcceptable(): bool {
|
|
|
|
return $this->query("SELECT pg_encoding_to_char(encoding) from pg_database where datname = current_database()")->getValue() == "UTF8";
|
2018-11-10 05:02:38 +00:00
|
|
|
}
|
|
|
|
|
2018-11-22 18:30:13 +00:00
|
|
|
public function savepointCreate(bool $lock = false): int {
|
|
|
|
if (!$this->transDepth) {
|
|
|
|
$this->exec("BEGIN TRANSACTION");
|
|
|
|
}
|
|
|
|
return parent::savepointCreate($lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function savepointRelease(int $index = null): bool {
|
|
|
|
$out = parent::savepointUndo($index);
|
|
|
|
if ($out && !$this->transDepth) {
|
|
|
|
$this->exec("COMMIT TRANSACTION");
|
|
|
|
}
|
|
|
|
return $out;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function savepointUndo(int $index = null): bool {
|
|
|
|
$out = parent::savepointUndo($index);
|
|
|
|
if ($out && !$this->transDepth) {
|
|
|
|
$this->exec("ROLLBACK TRANSACTION");
|
|
|
|
}
|
|
|
|
return $out;
|
|
|
|
}
|
|
|
|
|
2018-11-21 16:06:12 +00:00
|
|
|
protected function lock(): bool {
|
|
|
|
if ($this->schemaVersion()) {
|
|
|
|
$this->exec("LOCK TABLE arsse_meta IN EXCLUSIVE MODE NOWAIT");
|
|
|
|
}
|
|
|
|
return true;
|
2018-11-10 05:02:38 +00:00
|
|
|
}
|
|
|
|
|
2018-11-21 16:06:12 +00:00
|
|
|
protected function unlock(bool $rollback = false): bool {
|
|
|
|
$this->exec((!$rollback) ? "COMMIT" : "ROLLBACK");
|
2018-11-10 05:02:38 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-11-21 16:06:12 +00:00
|
|
|
public function __destruct() {
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function requirementsMet(): bool {
|
|
|
|
// stub: native interface is not yet supported
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function makeConnection(string $user, string $pass, string $db, string $host, int $port, string $service) {
|
|
|
|
// stub: native interface is not yet supported
|
|
|
|
throw new \Exception;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @codeCoverageIgnore */
|
2018-11-10 05:02:38 +00:00
|
|
|
protected function getError(): string {
|
2018-11-21 16:06:12 +00:00
|
|
|
// stub: native interface is not yet supported
|
2018-11-10 05:02:38 +00:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2018-11-21 16:06:12 +00:00
|
|
|
/** @codeCoverageIgnore */
|
2018-11-10 05:02:38 +00:00
|
|
|
public function exec(string $query): bool {
|
2018-11-21 16:06:12 +00:00
|
|
|
// stub: native interface is not yet supported
|
2018-11-10 05:02:38 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-11-21 16:06:12 +00:00
|
|
|
/** @codeCoverageIgnore */
|
2018-11-10 05:02:38 +00:00
|
|
|
public function query(string $query): \JKingWeb\Arsse\Db\Result {
|
2018-11-21 16:06:12 +00:00
|
|
|
// stub: native interface is not yet supported
|
2018-11-10 05:02:38 +00:00
|
|
|
return new ResultEmpty;
|
|
|
|
}
|
|
|
|
|
2018-11-21 16:06:12 +00:00
|
|
|
/** @codeCoverageIgnore */
|
2018-11-10 05:02:38 +00:00
|
|
|
public function prepareArray(string $query, array $paramTypes): \JKingWeb\Arsse\Db\Statement {
|
2018-11-21 16:06:12 +00:00
|
|
|
// stub: native interface is not yet supported
|
2018-11-10 05:02:38 +00:00
|
|
|
return new Statement($this->db, $s, $paramTypes);
|
|
|
|
}
|
|
|
|
}
|