%PDF- %PDF-
| Direktori : /www/varak.net/losik.varak.net/vendor/dibi/dibi/src/Dibi/Drivers/ |
| Current File : /www/varak.net/losik.varak.net/vendor/dibi/dibi/src/Dibi/Drivers/PostgreDriver.php |
<?php
/**
* This file is part of the Dibi, smart database abstraction layer (https://dibiphp.com)
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
declare(strict_types=1);
namespace Dibi\Drivers;
use Dibi;
use Dibi\Helpers;
use PgSql;
/**
* The driver for PostgreSQL database.
*
* Driver options:
* - host, hostaddr, port, dbname, user, password, connect_timeout, options, sslmode, service => see PostgreSQL API
* - string => or use connection string
* - schema => the schema search path
* - charset => character encoding to set (default is utf8)
* - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource
* - connect_type (int) => see pg_connect()
*/
class PostgreDriver implements Dibi\Driver
{
use Dibi\Strict;
/** @var resource|PgSql\Connection */
private $connection;
/** @var int|null Affected rows */
private $affectedRows;
/** @throws Dibi\NotSupportedException */
public function __construct(array $config)
{
if (!extension_loaded('pgsql')) {
throw new Dibi\NotSupportedException("PHP extension 'pgsql' is not loaded.");
}
$error = null;
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
$config += [
'charset' => 'utf8',
];
if (isset($config['string'])) {
$string = $config['string'];
} else {
$string = '';
Helpers::alias($config, 'user', 'username');
Helpers::alias($config, 'dbname', 'database');
foreach (['host', 'hostaddr', 'port', 'dbname', 'user', 'password', 'connect_timeout', 'options', 'sslmode', 'service'] as $key) {
if (isset($config[$key])) {
$string .= $key . '=' . $config[$key] . ' ';
}
}
}
$connectType = $config['connect_type'] ?? PGSQL_CONNECT_FORCE_NEW;
set_error_handler(function (int $severity, string $message) use (&$error) {
$error = $message;
});
$this->connection = empty($config['persistent'])
? pg_connect($string, $connectType)
: pg_pconnect($string, $connectType);
restore_error_handler();
}
if (!is_resource($this->connection) && !$this->connection instanceof PgSql\Connection) {
throw new Dibi\DriverException($error ?: 'Connecting error.');
}
pg_set_error_verbosity($this->connection, PGSQL_ERRORS_VERBOSE);
if (isset($config['charset']) && pg_set_client_encoding($this->connection, $config['charset'])) {
throw static::createException(pg_last_error($this->connection));
}
if (isset($config['schema'])) {
$this->query('SET search_path TO "' . implode('", "', (array) $config['schema']) . '"');
}
}
/**
* Disconnects from a database.
*/
public function disconnect(): void
{
@pg_close($this->connection); // @ - connection can be already disconnected
}
/**
* Pings database.
*/
public function ping(): bool
{
return pg_ping($this->connection);
}
/**
* Executes the SQL query.
* @throws Dibi\DriverException
*/
public function query(string $sql): ?Dibi\ResultDriver
{
$this->affectedRows = null;
$res = @pg_query($this->connection, $sql); // intentionally @
if ($res === false) {
throw static::createException(pg_last_error($this->connection), null, $sql);
} elseif (is_resource($res) || $res instanceof PgSql\Result) {
$this->affectedRows = Helpers::false2Null(pg_affected_rows($res));
if (pg_num_fields($res)) {
return $this->createResultDriver($res);
}
}
return null;
}
public static function createException(string $message, $code = null, ?string $sql = null): Dibi\DriverException
{
if ($code === null && preg_match('#^ERROR:\s+(\S+):\s*#', $message, $m)) {
$code = $m[1];
$message = substr($message, strlen($m[0]));
}
if ($code === '0A000' && strpos($message, 'truncate') !== false) {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} elseif ($code === '23502') {
return new Dibi\NotNullConstraintViolationException($message, $code, $sql);
} elseif ($code === '23503') {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} elseif ($code === '23505') {
return new Dibi\UniqueConstraintViolationException($message, $code, $sql);
} else {
return new Dibi\DriverException($message, $code, $sql);
}
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
*/
public function getAffectedRows(): ?int
{
return $this->affectedRows;
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
*/
public function getInsertId(?string $sequence): ?int
{
$res = $sequence === null
? $this->query('SELECT LASTVAL()') // PostgreSQL 8.1 is needed
: $this->query("SELECT CURRVAL('$sequence')");
if (!$res) {
return null;
}
$row = $res->fetch(false);
return is_array($row) ? (int) $row[0] : null;
}
/**
* Begins a transaction (if supported).
* @throws Dibi\DriverException
*/
public function begin(?string $savepoint = null): void
{
$this->query($savepoint ? "SAVEPOINT {$this->escapeIdentifier($savepoint)}" : 'START TRANSACTION');
}
/**
* Commits statements in a transaction.
* @throws Dibi\DriverException
*/
public function commit(?string $savepoint = null): void
{
$this->query($savepoint ? "RELEASE SAVEPOINT {$this->escapeIdentifier($savepoint)}" : 'COMMIT');
}
/**
* Rollback changes in a transaction.
* @throws Dibi\DriverException
*/
public function rollback(?string $savepoint = null): void
{
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT {$this->escapeIdentifier($savepoint)}" : 'ROLLBACK');
}
/**
* Is in transaction?
*/
public function inTransaction(): bool
{
return !in_array(pg_transaction_status($this->connection), [PGSQL_TRANSACTION_UNKNOWN, PGSQL_TRANSACTION_IDLE], true);
}
/**
* Returns the connection resource.
* @return resource|null
*/
public function getResource()
{
return is_resource($this->connection) || $this->connection instanceof PgSql\Connection
? $this->connection
: null;
}
/**
* Returns the connection reflector.
*/
public function getReflector(): Dibi\Reflector
{
return new PostgreReflector($this, pg_parameter_status($this->connection, 'server_version'));
}
/**
* Result set driver factory.
* @param resource $resource
*/
public function createResultDriver($resource): PostgreResult
{
return new PostgreResult($resource);
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
*/
public function escapeText(string $value): string
{
if (!$this->getResource()) {
throw new Dibi\Exception('Lost connection to server.');
}
return "'" . pg_escape_string($this->connection, $value) . "'";
}
public function escapeBinary(string $value): string
{
if (!$this->getResource()) {
throw new Dibi\Exception('Lost connection to server.');
}
return "'" . pg_escape_bytea($this->connection, $value) . "'";
}
public function escapeIdentifier(string $value): string
{
// @see http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
return '"' . str_replace('"', '""', $value) . '"';
}
public function escapeBool(bool $value): string
{
return $value ? 'TRUE' : 'FALSE';
}
public function escapeDate(\DateTimeInterface $value): string
{
return $value->format("'Y-m-d'");
}
public function escapeDateTime(\DateTimeInterface $value): string
{
return $value->format("'Y-m-d H:i:s.u'");
}
public function escapeDateInterval(\DateInterval $value): string
{
throw new Dibi\NotImplementedException;
}
/**
* Encodes string for use in a LIKE statement.
*/
public function escapeLike(string $value, int $pos): string
{
$bs = pg_escape_string($this->connection, '\\'); // standard_conforming_strings = on/off
$value = pg_escape_string($this->connection, $value);
$value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']);
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
}
/**
* Injects LIMIT/OFFSET to the SQL query.
*/
public function applyLimit(string &$sql, ?int $limit, ?int $offset): void
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
}
if ($limit !== null) {
$sql .= ' LIMIT ' . $limit;
}
if ($offset) {
$sql .= ' OFFSET ' . $offset;
}
}
}