FileMaster
Search
Toggle Dark Mode
Home
/
.
/
wp-content
/
plugins
/
mailpoet
/
vendor-prefixed
/
doctrine
/
dbal
/
src
/
Schema
Edit File: PostgreSQLSchemaManager.php
<?php namespace MailPoetVendor\Doctrine\DBAL\Schema; if (!defined('ABSPATH')) exit; use MailPoetVendor\Doctrine\DBAL\Exception; use MailPoetVendor\Doctrine\DBAL\Platforms\PostgreSQLPlatform; use MailPoetVendor\Doctrine\DBAL\Result; use MailPoetVendor\Doctrine\DBAL\Types\JsonType; use MailPoetVendor\Doctrine\DBAL\Types\Type; use MailPoetVendor\Doctrine\DBAL\Types\Types; use MailPoetVendor\Doctrine\Deprecations\Deprecation; use function array_change_key_case; use function array_filter; use function array_map; use function array_merge; use function array_shift; use function assert; use function explode; use function get_class; use function implode; use function in_array; use function preg_match; use function preg_replace; use function sprintf; use function str_replace; use function strpos; use function strtolower; use function trim; use const CASE_LOWER; class PostgreSQLSchemaManager extends AbstractSchemaManager { private ?array $existingSchemaPaths = null; public function listTableNames() { return $this->doListTableNames(); } public function listTables() { return $this->doListTables(); } public function listTableDetails($name) { Deprecation::triggerIfCalledFromOutside('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5595', '%s is deprecated. Use introspectTable() instead.', __METHOD__); return $this->doListTableDetails($name); } public function listTableColumns($table, $database = null) { return $this->doListTableColumns($table, $database); } public function listTableIndexes($table) { return $this->doListTableIndexes($table); } public function listTableForeignKeys($table, $database = null) { return $this->doListTableForeignKeys($table, $database); } public function getSchemaNames() { Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4503', 'PostgreSQLSchemaManager::getSchemaNames() is deprecated,' . ' use PostgreSQLSchemaManager::listSchemaNames() instead.'); return $this->listNamespaceNames(); } public function listSchemaNames() : array { return $this->_conn->fetchFirstColumn(<<<'SQL' SELECT schema_name FROM information_schema.schemata WHERE schema_name NOT LIKE 'pg\_%' AND schema_name != 'information_schema' SQL ); } public function getSchemaSearchPaths() { Deprecation::triggerIfCalledFromOutside('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4821', 'PostgreSQLSchemaManager::getSchemaSearchPaths() is deprecated.'); $params = $this->_conn->getParams(); $searchPaths = $this->_conn->fetchOne('SHOW search_path'); assert($searchPaths !== \false); $schema = explode(',', $searchPaths); if (isset($params['user'])) { $schema = str_replace('"$user"', $params['user'], $schema); } return array_map('trim', $schema); } public function getExistingSchemaSearchPaths() { if ($this->existingSchemaPaths === null) { $this->determineExistingSchemaSearchPaths(); } assert($this->existingSchemaPaths !== null); return $this->existingSchemaPaths; } protected function getCurrentSchema() { $schemas = $this->getExistingSchemaSearchPaths(); return array_shift($schemas); } public function determineExistingSchemaSearchPaths() { $names = $this->listSchemaNames(); $paths = $this->getSchemaSearchPaths(); $this->existingSchemaPaths = array_filter($paths, static function ($v) use($names) : bool { return in_array($v, $names, \true); }); } protected function _getPortableTableForeignKeyDefinition($tableForeignKey) { $onUpdate = null; $onDelete = null; if (preg_match('(ON UPDATE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], $match) === 1) { $onUpdate = $match[1]; } if (preg_match('(ON DELETE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], $match) === 1) { $onDelete = $match[1]; } $result = preg_match('/FOREIGN KEY \\((.+)\\) REFERENCES (.+)\\((.+)\\)/', $tableForeignKey['condef'], $values); assert($result === 1); // PostgreSQL returns identifiers that are keywords with quotes, we need them later, don't get // the idea to trim them here. $localColumns = array_map('trim', explode(',', $values[1])); $foreignColumns = array_map('trim', explode(',', $values[3])); $foreignTable = $values[2]; return new ForeignKeyConstraint($localColumns, $foreignTable, $foreignColumns, $tableForeignKey['conname'], ['onUpdate' => $onUpdate, 'onDelete' => $onDelete]); } protected function _getPortableViewDefinition($view) { return new View($view['schemaname'] . '.' . $view['viewname'], $view['definition']); } protected function _getPortableTableDefinition($table) { $currentSchema = $this->getCurrentSchema(); if ($table['schema_name'] === $currentSchema) { return $table['table_name']; } return $table['schema_name'] . '.' . $table['table_name']; } protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) { $buffer = []; foreach ($tableIndexes as $row) { $colNumbers = array_map('intval', explode(' ', $row['indkey'])); $columnNameSql = sprintf(<<<'SQL' SELECT attnum, quote_ident(attname) AS attname FROM pg_attribute WHERE attrelid = %d AND attnum IN (%s) ORDER BY attnum SQL , $row['indrelid'], implode(', ', $colNumbers)); $indexColumns = $this->_conn->fetchAllAssociative($columnNameSql); // required for getting the order of the columns right. foreach ($colNumbers as $colNum) { foreach ($indexColumns as $colRow) { if ($colNum !== $colRow['attnum']) { continue; } $buffer[] = ['key_name' => $row['relname'], 'column_name' => trim($colRow['attname']), 'non_unique' => !$row['indisunique'], 'primary' => $row['indisprimary'], 'where' => $row['where']]; } } } return parent::_getPortableTableIndexesList($buffer, $tableName); } protected function _getPortableDatabaseDefinition($database) { return $database['datname']; } protected function getPortableNamespaceDefinition(array $namespace) { Deprecation::triggerIfCalledFromOutside('doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4503', 'PostgreSQLSchemaManager::getPortableNamespaceDefinition() is deprecated,' . ' use PostgreSQLSchemaManager::listSchemaNames() instead.'); return $namespace['nspname']; } protected function _getPortableSequenceDefinition($sequence) { if ($sequence['schemaname'] !== 'public') { $sequenceName = $sequence['schemaname'] . '.' . $sequence['relname']; } else { $sequenceName = $sequence['relname']; } return new Sequence($sequenceName, (int) $sequence['increment_by'], (int) $sequence['min_value']); } protected function _getPortableTableColumnDefinition($tableColumn) { $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); if (strtolower($tableColumn['type']) === 'varchar' || strtolower($tableColumn['type']) === 'bpchar') { // get length from varchar definition $length = preg_replace('~.*\\(([0-9]*)\\).*~', '$1', $tableColumn['complete_type']); $tableColumn['length'] = $length; } $matches = []; $autoincrement = \false; if ($tableColumn['default'] !== null && preg_match("/^nextval\\('(.*)'(::.*)?\\)\$/", $tableColumn['default'], $matches) === 1) { $tableColumn['sequence'] = $matches[1]; $tableColumn['default'] = null; $autoincrement = \true; } if ($tableColumn['default'] !== null) { if (preg_match("/^['(](.*)[')]::/", $tableColumn['default'], $matches) === 1) { $tableColumn['default'] = $matches[1]; } elseif (preg_match('/^NULL::/', $tableColumn['default']) === 1) { $tableColumn['default'] = null; } } $length = $tableColumn['length'] ?? null; if ($length === '-1' && isset($tableColumn['atttypmod'])) { $length = $tableColumn['atttypmod'] - 4; } if ((int) $length <= 0) { $length = null; } $fixed = null; if (!isset($tableColumn['name'])) { $tableColumn['name'] = ''; } $precision = null; $scale = null; $jsonb = null; $dbType = strtolower($tableColumn['type']); if ($tableColumn['domain_type'] !== null && $tableColumn['domain_type'] !== '' && !$this->_platform->hasDoctrineTypeMappingFor($tableColumn['type'])) { $dbType = strtolower($tableColumn['domain_type']); $tableColumn['complete_type'] = $tableColumn['domain_complete_type']; } $type = $this->_platform->getDoctrineTypeMapping($dbType); $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); switch ($dbType) { case 'smallint': case 'int2': $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); $length = null; break; case 'int': case 'int4': case 'integer': $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); $length = null; break; case 'bigint': case 'int8': $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); $length = null; break; case 'bool': case 'boolean': if ($tableColumn['default'] === 'true') { $tableColumn['default'] = \true; } if ($tableColumn['default'] === 'false') { $tableColumn['default'] = \false; } $length = null; break; case 'json': case 'text': case '_varchar': case 'varchar': $tableColumn['default'] = $this->parseDefaultExpression($tableColumn['default']); $fixed = \false; break; case 'interval': $fixed = \false; break; case 'char': case 'bpchar': $fixed = \true; break; case 'float': case 'float4': case 'float8': case 'double': case 'double precision': case 'real': case 'decimal': case 'money': case 'numeric': $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); if (preg_match('([A-Za-z]+\\(([0-9]+),([0-9]+)\\))', $tableColumn['complete_type'], $match) === 1) { $precision = $match[1]; $scale = $match[2]; $length = null; } break; case 'year': $length = null; break; // PostgreSQL 9.4+ only case 'jsonb': $jsonb = \true; break; } if ($tableColumn['default'] !== null && preg_match("('([^']+)'::)", $tableColumn['default'], $match) === 1) { $tableColumn['default'] = $match[1]; } $options = ['length' => $length, 'notnull' => (bool) $tableColumn['isnotnull'], 'default' => $tableColumn['default'], 'precision' => $precision, 'scale' => $scale, 'fixed' => $fixed, 'autoincrement' => $autoincrement, 'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== '' ? $tableColumn['comment'] : null]; $column = new Column($tableColumn['field'], Type::getType($type), $options); if (!empty($tableColumn['collation'])) { $column->setPlatformOption('collation', $tableColumn['collation']); } if ($column->getType()->getName() === Types::JSON) { if (!$column->getType() instanceof JsonType) { Deprecation::trigger('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5049', <<<'DEPRECATION' %s not extending %s while being named %s is deprecated, and will lead to jsonb never to being used in 4.0., DEPRECATION , get_class($column->getType()), JsonType::class, Types::JSON); } $column->setPlatformOption('jsonb', $jsonb); } return $column; } private function fixVersion94NegativeNumericDefaultValue($defaultValue) { if ($defaultValue !== null && strpos($defaultValue, '(') === 0) { return trim($defaultValue, '()'); } return $defaultValue; } private function parseDefaultExpression(?string $default) : ?string { if ($default === null) { return $default; } return str_replace("''", "'", $default); } protected function selectTableNames(string $databaseName) : Result { $sql = <<<'SQL' SELECT quote_ident(table_name) AS table_name, table_schema AS schema_name FROM information_schema.tables WHERE table_catalog = ? AND table_schema NOT LIKE 'pg\_%' AND table_schema != 'information_schema' AND table_name != 'geometry_columns' AND table_name != 'spatial_ref_sys' AND table_type = 'BASE TABLE' ORDER BY quote_ident(table_name) SQL; return $this->_conn->executeQuery($sql, [$databaseName]); } protected function selectTableColumns(string $databaseName, ?string $tableName = null) : Result { $sql = 'SELECT'; if ($tableName === null) { $sql .= ' quote_ident(c.relname) AS table_name, quote_ident(n.nspname) AS schema_name,'; } $sql .= sprintf(<<<'SQL' a.attnum, quote_ident(a.attname) AS field, t.typname AS type, format_type(a.atttypid, a.atttypmod) AS complete_type, (SELECT tc.collcollate FROM pg_catalog.pg_collation tc WHERE tc.oid = a.attcollation) AS collation, (SELECT t1.typname FROM pg_catalog.pg_type t1 WHERE t1.oid = t.typbasetype) AS domain_type, (SELECT format_type(t2.typbasetype, t2.typtypmod) FROM pg_catalog.pg_type t2 WHERE t2.typtype = 'd' AND t2.oid = a.atttypid) AS domain_complete_type, a.attnotnull AS isnotnull, (SELECT 't' FROM pg_index WHERE c.oid = pg_index.indrelid AND pg_index.indkey[0] = a.attnum AND pg_index.indisprimary = 't' ) AS pri, (%s) AS default, (SELECT pg_description.description FROM pg_description WHERE pg_description.objoid = c.oid AND a.attnum = pg_description.objsubid ) AS comment FROM pg_attribute a INNER JOIN pg_class c ON c.oid = a.attrelid INNER JOIN pg_type t ON t.oid = a.atttypid INNER JOIN pg_namespace n ON n.oid = c.relnamespace LEFT JOIN pg_depend d ON d.objid = c.oid AND d.deptype = 'e' AND d.classid = (SELECT oid FROM pg_class WHERE relname = 'pg_class') SQL , $this->_platform->getDefaultColumnValueSQLSnippet()); $conditions = array_merge(['a.attnum > 0', "c.relkind = 'r'", 'd.refobjid IS NULL'], $this->buildQueryConditions($tableName)); $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY a.attnum'; return $this->_conn->executeQuery($sql); } protected function selectIndexColumns(string $databaseName, ?string $tableName = null) : Result { $sql = 'SELECT'; if ($tableName === null) { $sql .= ' quote_ident(tc.relname) AS table_name, quote_ident(tn.nspname) AS schema_name,'; } $sql .= <<<'SQL' quote_ident(ic.relname) AS relname, i.indisunique, i.indisprimary, i.indkey, i.indrelid, pg_get_expr(indpred, indrelid) AS "where" FROM pg_index i JOIN pg_class AS tc ON tc.oid = i.indrelid JOIN pg_namespace tn ON tn.oid = tc.relnamespace JOIN pg_class AS ic ON ic.oid = i.indexrelid WHERE ic.oid IN ( SELECT indexrelid FROM pg_index i, pg_class c, pg_namespace n SQL; $conditions = array_merge(['c.oid = i.indrelid', 'c.relnamespace = n.oid'], $this->buildQueryConditions($tableName)); $sql .= ' WHERE ' . implode(' AND ', $conditions) . ') ORDER BY quote_ident(ic.relname)'; return $this->_conn->executeQuery($sql); } protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null) : Result { $sql = 'SELECT'; if ($tableName === null) { $sql .= ' quote_ident(tc.relname) AS table_name, quote_ident(tn.nspname) AS schema_name,'; } $sql .= <<<'SQL' quote_ident(r.conname) as conname, pg_get_constraintdef(r.oid, true) as condef FROM pg_constraint r JOIN pg_class AS tc ON tc.oid = r.conrelid JOIN pg_namespace tn ON tn.oid = tc.relnamespace WHERE r.conrelid IN ( SELECT c.oid FROM pg_class c, pg_namespace n SQL; $conditions = array_merge(['n.oid = c.relnamespace'], $this->buildQueryConditions($tableName)); $sql .= ' WHERE ' . implode(' AND ', $conditions) . ") AND r.contype = 'f' ORDER BY quote_ident(r.conname)"; return $this->_conn->executeQuery($sql); } protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null) : array { $sql = <<<'SQL' SELECT n.nspname AS schema_name, c.relname AS table_name, CASE c.relpersistence WHEN 'u' THEN true ELSE false END as unlogged, obj_description(c.oid, 'pg_class') AS comment FROM pg_class c INNER JOIN pg_namespace n ON n.oid = c.relnamespace SQL; $conditions = array_merge(["c.relkind = 'r'"], $this->buildQueryConditions($tableName)); $sql .= ' WHERE ' . implode(' AND ', $conditions); $tableOptions = []; foreach ($this->_conn->iterateAssociative($sql) as $row) { $tableOptions[$this->_getPortableTableDefinition($row)] = $row; } return $tableOptions; } private function buildQueryConditions($tableName) : array { $conditions = []; if ($tableName !== null) { if (strpos($tableName, '.') !== \false) { [$schemaName, $tableName] = explode('.', $tableName); $conditions[] = 'n.nspname = ' . $this->_platform->quoteStringLiteral($schemaName); } else { $conditions[] = 'n.nspname = ANY(current_schemas(false))'; } $identifier = new Identifier($tableName); $conditions[] = 'c.relname = ' . $this->_platform->quoteStringLiteral($identifier->getName()); } $conditions[] = "n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')"; return $conditions; } }
Save
Back