%PDF- %PDF-
Direktori : /www/varak.net/losik.varak.net/vendor/nette/database/src/Database/Drivers/ |
Current File : //www/varak.net/losik.varak.net/vendor/nette/database/src/Database/Drivers/PgSqlDriver.php |
<?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Database\Drivers; use Nette; /** * Supplemental PostgreSQL database driver. */ class PgSqlDriver implements Nette\Database\Driver { use Nette\SmartObject; /** @var Nette\Database\Connection */ private $connection; public function initialize(Nette\Database\Connection $connection, array $options): void { $this->connection = $connection; } public function convertException(\PDOException $e): Nette\Database\DriverException { $code = $e->errorInfo[0] ?? null; if ($code === '0A000' && strpos($e->getMessage(), 'truncate') !== false) { return Nette\Database\ForeignKeyConstraintViolationException::from($e); } elseif ($code === '23502') { return Nette\Database\NotNullConstraintViolationException::from($e); } elseif ($code === '23503') { return Nette\Database\ForeignKeyConstraintViolationException::from($e); } elseif ($code === '23505') { return Nette\Database\UniqueConstraintViolationException::from($e); } elseif ($code === '08006') { return Nette\Database\ConnectionException::from($e); } else { return Nette\Database\DriverException::from($e); } } /********************* SQL ****************d*g**/ public function delimite(string $name): string { // @see http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS return '"' . str_replace('"', '""', $name) . '"'; } public function formatDateTime(\DateTimeInterface $value): string { return $value->format("'Y-m-d H:i:s'"); } public function formatDateInterval(\DateInterval $value): string { throw new Nette\NotSupportedException; } public function formatLike(string $value, int $pos): string { $bs = substr($this->connection->quote('\\'), 1, -1); // standard_conforming_strings = on/off $value = substr($this->connection->quote($value), 1, -1); $value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']); return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); } public function applyLimit(string &$sql, ?int $limit, ?int $offset): void { if ($limit < 0 || $offset < 0) { throw new Nette\InvalidArgumentException('Negative offset or limit.'); } if ($limit !== null) { $sql .= ' LIMIT ' . $limit; } if ($offset) { $sql .= ' OFFSET ' . $offset; } } /********************* reflection ****************d*g**/ public function getTables(): array { $tables = []; foreach ($this->connection->query(" SELECT DISTINCT ON (c.relname) c.relname::varchar AS name, c.relkind IN ('v', 'm') AS view, n.nspname::varchar || '.' || c.relname::varchar AS \"fullName\" FROM pg_catalog.pg_class AS c JOIN pg_catalog.pg_namespace AS n ON n.oid = c.relnamespace WHERE c.relkind IN ('r', 'v', 'm', 'p') AND n.nspname = ANY (pg_catalog.current_schemas(FALSE)) ORDER BY c.relname ") as $row) { $tables[] = (array) $row; } return $tables; } public function getColumns(string $table): array { $columns = []; foreach ($this->connection->query(" SELECT a.attname::varchar AS name, c.relname::varchar AS table, upper(t.typname) AS nativetype, CASE WHEN a.atttypmod = -1 THEN NULL ELSE a.atttypmod -4 END AS size, NOT (a.attnotnull OR t.typtype = 'd' AND t.typnotnull) AS nullable, pg_catalog.pg_get_expr(ad.adbin, 'pg_catalog.pg_attrdef'::regclass)::varchar AS default, coalesce(co.contype = 'p' AND (seq.relname IS NOT NULL OR strpos(pg_catalog.pg_get_expr(ad.adbin, ad.adrelid), 'nextval') = 1), FALSE) AS autoincrement, coalesce(co.contype = 'p', FALSE) AS primary, coalesce(seq.relname, substring(pg_catalog.pg_get_expr(ad.adbin, 'pg_catalog.pg_attrdef'::regclass) from 'nextval[(]''\"?([^''\"]+)')) AS sequence FROM pg_catalog.pg_attribute AS a JOIN pg_catalog.pg_class AS c ON a.attrelid = c.oid JOIN pg_catalog.pg_type AS t ON a.atttypid = t.oid LEFT JOIN pg_catalog.pg_depend AS d ON d.refobjid = c.oid AND d.refobjsubid = a.attnum AND d.deptype = 'i' LEFT JOIN pg_catalog.pg_class AS seq ON seq.oid = d.objid AND seq.relkind = 'S' LEFT JOIN pg_catalog.pg_attrdef AS ad ON ad.adrelid = c.oid AND ad.adnum = a.attnum LEFT JOIN pg_catalog.pg_constraint AS co ON co.connamespace = c.relnamespace AND contype = 'p' AND co.conrelid = c.oid AND a.attnum = ANY(co.conkey) WHERE c.relkind IN ('r', 'v', 'm', 'p') AND c.oid = {$this->connection->quote($this->delimiteFQN($table))}::regclass AND a.attnum > 0 AND NOT a.attisdropped ORDER BY a.attnum ") as $row) { $column = (array) $row; $column['vendor'] = $column; unset($column['sequence']); $columns[] = $column; } return $columns; } public function getIndexes(string $table): array { $indexes = []; foreach ($this->connection->query(" SELECT c2.relname::varchar AS name, i.indisunique AS unique, i.indisprimary AS primary, a.attname::varchar AS column FROM pg_catalog.pg_class AS c1 JOIN pg_catalog.pg_index AS i ON c1.oid = i.indrelid JOIN pg_catalog.pg_class AS c2 ON i.indexrelid = c2.oid LEFT JOIN pg_catalog.pg_attribute AS a ON c1.oid = a.attrelid AND a.attnum = ANY(i.indkey) WHERE c1.relkind IN ('r', 'p') AND c1.oid = {$this->connection->quote($this->delimiteFQN($table))}::regclass ") as $row) { $id = $row['name']; $indexes[$id]['name'] = $id; $indexes[$id]['unique'] = $row['unique']; $indexes[$id]['primary'] = $row['primary']; $indexes[$id]['columns'][] = $row['column']; } return array_values($indexes); } public function getForeignKeys(string $table): array { /* Does't work with multicolumn foreign keys */ return $this->connection->query(" SELECT co.conname::varchar AS name, al.attname::varchar AS local, nf.nspname || '.' || cf.relname::varchar AS table, af.attname::varchar AS foreign FROM pg_catalog.pg_constraint AS co JOIN pg_catalog.pg_class AS cl ON co.conrelid = cl.oid JOIN pg_catalog.pg_class AS cf ON co.confrelid = cf.oid JOIN pg_catalog.pg_namespace AS nf ON nf.oid = cf.relnamespace JOIN pg_catalog.pg_attribute AS al ON al.attrelid = cl.oid AND al.attnum = co.conkey[1] JOIN pg_catalog.pg_attribute AS af ON af.attrelid = cf.oid AND af.attnum = co.confkey[1] WHERE co.contype = 'f' AND cl.oid = {$this->connection->quote($this->delimiteFQN($table))}::regclass AND nf.nspname = ANY (pg_catalog.current_schemas(FALSE)) ")->fetchAll(); } public function getColumnTypes(\PDOStatement $statement): array { static $cache; $item = &$cache[$statement->queryString]; if ($item === null) { $item = Nette\Database\Helpers::detectTypes($statement); } return $item; } public function isSupported(string $item): bool { return $item === self::SUPPORT_SEQUENCE || $item === self::SUPPORT_SUBSELECT || $item === self::SUPPORT_SCHEMA; } /** * Converts: schema.name => "schema"."name" */ private function delimiteFQN(string $name): string { return implode('.', array_map([$this, 'delimite'], explode('.', $name))); } }