vendor/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php line 46

Open in your IDE?
  1. <?php
  2. namespace Doctrine\DBAL\Schema;
  3. use Doctrine\DBAL\Exception;
  4. use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
  5. use Doctrine\DBAL\Result;
  6. use Doctrine\DBAL\Types\JsonType;
  7. use Doctrine\DBAL\Types\Type;
  8. use Doctrine\DBAL\Types\Types;
  9. use Doctrine\Deprecations\Deprecation;
  10. use function array_change_key_case;
  11. use function array_filter;
  12. use function array_map;
  13. use function array_merge;
  14. use function array_shift;
  15. use function assert;
  16. use function explode;
  17. use function get_class;
  18. use function implode;
  19. use function in_array;
  20. use function preg_match;
  21. use function preg_replace;
  22. use function sprintf;
  23. use function str_replace;
  24. use function strpos;
  25. use function strtolower;
  26. use function trim;
  27. use const CASE_LOWER;
  28. /**
  29.  * PostgreSQL Schema Manager.
  30.  *
  31.  * @extends AbstractSchemaManager<PostgreSQLPlatform>
  32.  */
  33. class PostgreSQLSchemaManager extends AbstractSchemaManager
  34. {
  35.     /** @var string[]|null */
  36.     private ?array $existingSchemaPaths null;
  37.     /**
  38.      * {@inheritDoc}
  39.      */
  40.     public function listTableNames()
  41.     {
  42.         return $this->doListTableNames();
  43.     }
  44.     /**
  45.      * {@inheritDoc}
  46.      */
  47.     public function listTables()
  48.     {
  49.         return $this->doListTables();
  50.     }
  51.     /**
  52.      * {@inheritDoc}
  53.      */
  54.     public function listTableDetails($name)
  55.     {
  56.         return $this->doListTableDetails($name);
  57.     }
  58.     /**
  59.      * {@inheritDoc}
  60.      */
  61.     public function listTableColumns($table$database null)
  62.     {
  63.         return $this->doListTableColumns($table$database);
  64.     }
  65.     /**
  66.      * {@inheritDoc}
  67.      */
  68.     public function listTableIndexes($table)
  69.     {
  70.         return $this->doListTableIndexes($table);
  71.     }
  72.     /**
  73.      * {@inheritDoc}
  74.      */
  75.     public function listTableForeignKeys($table$database null)
  76.     {
  77.         return $this->doListTableForeignKeys($table$database);
  78.     }
  79.     /**
  80.      * Gets all the existing schema names.
  81.      *
  82.      * @deprecated Use {@see listSchemaNames()} instead.
  83.      *
  84.      * @return string[]
  85.      *
  86.      * @throws Exception
  87.      */
  88.     public function getSchemaNames()
  89.     {
  90.         Deprecation::trigger(
  91.             'doctrine/dbal',
  92.             'https://github.com/doctrine/dbal/issues/4503',
  93.             'PostgreSQLSchemaManager::getSchemaNames() is deprecated,'
  94.                 ' use PostgreSQLSchemaManager::listSchemaNames() instead.'
  95.         );
  96.         return $this->listNamespaceNames();
  97.     }
  98.     /**
  99.      * {@inheritDoc}
  100.      */
  101.     public function listSchemaNames(): array
  102.     {
  103.         return $this->_conn->fetchFirstColumn(
  104.             <<<'SQL'
  105. SELECT schema_name
  106. FROM   information_schema.schemata
  107. WHERE  schema_name NOT LIKE 'pg\_%'
  108. AND    schema_name != 'information_schema'
  109. SQL
  110.         );
  111.     }
  112.     /**
  113.      * {@inheritDoc}
  114.      *
  115.      * @deprecated
  116.      */
  117.     public function getSchemaSearchPaths()
  118.     {
  119.         Deprecation::triggerIfCalledFromOutside(
  120.             'doctrine/dbal',
  121.             'https://github.com/doctrine/dbal/pull/4821',
  122.             'PostgreSQLSchemaManager::getSchemaSearchPaths() is deprecated.'
  123.         );
  124.         $params $this->_conn->getParams();
  125.         $searchPaths $this->_conn->fetchOne('SHOW search_path');
  126.         assert($searchPaths !== false);
  127.         $schema explode(','$searchPaths);
  128.         if (isset($params['user'])) {
  129.             $schema str_replace('"$user"'$params['user'], $schema);
  130.         }
  131.         return array_map('trim'$schema);
  132.     }
  133.     /**
  134.      * Gets names of all existing schemas in the current users search path.
  135.      *
  136.      * This is a PostgreSQL only function.
  137.      *
  138.      * @internal The method should be only used from within the PostgreSQLSchemaManager class hierarchy.
  139.      *
  140.      * @return string[]
  141.      *
  142.      * @throws Exception
  143.      */
  144.     public function getExistingSchemaSearchPaths()
  145.     {
  146.         if ($this->existingSchemaPaths === null) {
  147.             $this->determineExistingSchemaSearchPaths();
  148.         }
  149.         assert($this->existingSchemaPaths !== null);
  150.         return $this->existingSchemaPaths;
  151.     }
  152.     /**
  153.      * Returns the name of the current schema.
  154.      *
  155.      * @return string|null
  156.      *
  157.      * @throws Exception
  158.      */
  159.     protected function getCurrentSchema()
  160.     {
  161.         $schemas $this->getExistingSchemaSearchPaths();
  162.         return array_shift($schemas);
  163.     }
  164.     /**
  165.      * Sets or resets the order of the existing schemas in the current search path of the user.
  166.      *
  167.      * This is a PostgreSQL only function.
  168.      *
  169.      * @internal The method should be only used from within the PostgreSQLSchemaManager class hierarchy.
  170.      *
  171.      * @return void
  172.      *
  173.      * @throws Exception
  174.      */
  175.     public function determineExistingSchemaSearchPaths()
  176.     {
  177.         $names $this->listSchemaNames();
  178.         $paths $this->getSchemaSearchPaths();
  179.         $this->existingSchemaPaths array_filter($paths, static function ($v) use ($names): bool {
  180.             return in_array($v$namestrue);
  181.         });
  182.     }
  183.     /**
  184.      * {@inheritdoc}
  185.      */
  186.     protected function _getPortableTableForeignKeyDefinition($tableForeignKey)
  187.     {
  188.         $onUpdate null;
  189.         $onDelete null;
  190.         if (
  191.             preg_match(
  192.                 '(ON UPDATE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))',
  193.                 $tableForeignKey['condef'],
  194.                 $match
  195.             ) === 1
  196.         ) {
  197.             $onUpdate $match[1];
  198.         }
  199.         if (
  200.             preg_match(
  201.                 '(ON DELETE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))',
  202.                 $tableForeignKey['condef'],
  203.                 $match
  204.             ) === 1
  205.         ) {
  206.             $onDelete $match[1];
  207.         }
  208.         $result preg_match('/FOREIGN KEY \((.+)\) REFERENCES (.+)\((.+)\)/'$tableForeignKey['condef'], $values);
  209.         assert($result === 1);
  210.         // PostgreSQL returns identifiers that are keywords with quotes, we need them later, don't get
  211.         // the idea to trim them here.
  212.         $localColumns   array_map('trim'explode(','$values[1]));
  213.         $foreignColumns array_map('trim'explode(','$values[3]));
  214.         $foreignTable   $values[2];
  215.         return new ForeignKeyConstraint(
  216.             $localColumns,
  217.             $foreignTable,
  218.             $foreignColumns,
  219.             $tableForeignKey['conname'],
  220.             ['onUpdate' => $onUpdate'onDelete' => $onDelete]
  221.         );
  222.     }
  223.     /**
  224.      * {@inheritdoc}
  225.      */
  226.     protected function _getPortableViewDefinition($view)
  227.     {
  228.         return new View($view['schemaname'] . '.' $view['viewname'], $view['definition']);
  229.     }
  230.     /**
  231.      * {@inheritdoc}
  232.      */
  233.     protected function _getPortableTableDefinition($table)
  234.     {
  235.         $currentSchema $this->getCurrentSchema();
  236.         if ($table['schema_name'] === $currentSchema) {
  237.             return $table['table_name'];
  238.         }
  239.         return $table['schema_name'] . '.' $table['table_name'];
  240.     }
  241.     /**
  242.      * {@inheritdoc}
  243.      *
  244.      * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html
  245.      */
  246.     protected function _getPortableTableIndexesList($tableIndexes$tableName null)
  247.     {
  248.         $buffer = [];
  249.         foreach ($tableIndexes as $row) {
  250.             $colNumbers    array_map('intval'explode(' '$row['indkey']));
  251.             $columnNameSql sprintf(
  252.                 'SELECT attnum, attname FROM pg_attribute WHERE attrelid=%d AND attnum IN (%s) ORDER BY attnum ASC',
  253.                 $row['indrelid'],
  254.                 implode(' ,'$colNumbers)
  255.             );
  256.             $indexColumns $this->_conn->fetchAllAssociative($columnNameSql);
  257.             // required for getting the order of the columns right.
  258.             foreach ($colNumbers as $colNum) {
  259.                 foreach ($indexColumns as $colRow) {
  260.                     if ($colNum !== $colRow['attnum']) {
  261.                         continue;
  262.                     }
  263.                     $buffer[] = [
  264.                         'key_name' => $row['relname'],
  265.                         'column_name' => trim($colRow['attname']),
  266.                         'non_unique' => ! $row['indisunique'],
  267.                         'primary' => $row['indisprimary'],
  268.                         'where' => $row['where'],
  269.                     ];
  270.                 }
  271.             }
  272.         }
  273.         return parent::_getPortableTableIndexesList($buffer$tableName);
  274.     }
  275.     /**
  276.      * {@inheritdoc}
  277.      */
  278.     protected function _getPortableDatabaseDefinition($database)
  279.     {
  280.         return $database['datname'];
  281.     }
  282.     /**
  283.      * {@inheritdoc}
  284.      *
  285.      * @deprecated Use {@see listSchemaNames()} instead.
  286.      */
  287.     protected function getPortableNamespaceDefinition(array $namespace)
  288.     {
  289.         Deprecation::triggerIfCalledFromOutside(
  290.             'doctrine/dbal',
  291.             'https://github.com/doctrine/dbal/issues/4503',
  292.             'PostgreSQLSchemaManager::getPortableNamespaceDefinition() is deprecated,'
  293.                 ' use PostgreSQLSchemaManager::listSchemaNames() instead.'
  294.         );
  295.         return $namespace['nspname'];
  296.     }
  297.     /**
  298.      * {@inheritdoc}
  299.      */
  300.     protected function _getPortableSequenceDefinition($sequence)
  301.     {
  302.         if ($sequence['schemaname'] !== 'public') {
  303.             $sequenceName $sequence['schemaname'] . '.' $sequence['relname'];
  304.         } else {
  305.             $sequenceName $sequence['relname'];
  306.         }
  307.         return new Sequence($sequenceName, (int) $sequence['increment_by'], (int) $sequence['min_value']);
  308.     }
  309.     /**
  310.      * {@inheritdoc}
  311.      */
  312.     protected function _getPortableTableColumnDefinition($tableColumn)
  313.     {
  314.         $tableColumn array_change_key_case($tableColumnCASE_LOWER);
  315.         if (strtolower($tableColumn['type']) === 'varchar' || strtolower($tableColumn['type']) === 'bpchar') {
  316.             // get length from varchar definition
  317.             $length                preg_replace('~.*\(([0-9]*)\).*~''$1'$tableColumn['complete_type']);
  318.             $tableColumn['length'] = $length;
  319.         }
  320.         $matches = [];
  321.         $autoincrement false;
  322.         if (
  323.             $tableColumn['default'] !== null
  324.             && preg_match("/^nextval\('(.*)'(::.*)?\)$/"$tableColumn['default'], $matches) === 1
  325.         ) {
  326.             $tableColumn['sequence'] = $matches[1];
  327.             $tableColumn['default']  = null;
  328.             $autoincrement           true;
  329.         }
  330.         if ($tableColumn['default'] !== null) {
  331.             if (preg_match("/^['(](.*)[')]::/"$tableColumn['default'], $matches) === 1) {
  332.                 $tableColumn['default'] = $matches[1];
  333.             } elseif (preg_match('/^NULL::/'$tableColumn['default']) === 1) {
  334.                 $tableColumn['default'] = null;
  335.             }
  336.         }
  337.         $length $tableColumn['length'] ?? null;
  338.         if ($length === '-1' && isset($tableColumn['atttypmod'])) {
  339.             $length $tableColumn['atttypmod'] - 4;
  340.         }
  341.         if ((int) $length <= 0) {
  342.             $length null;
  343.         }
  344.         $fixed null;
  345.         if (! isset($tableColumn['name'])) {
  346.             $tableColumn['name'] = '';
  347.         }
  348.         $precision null;
  349.         $scale     null;
  350.         $jsonb     null;
  351.         $dbType strtolower($tableColumn['type']);
  352.         if (
  353.             $tableColumn['domain_type'] !== null
  354.             && $tableColumn['domain_type'] !== ''
  355.             && ! $this->_platform->hasDoctrineTypeMappingFor($tableColumn['type'])
  356.         ) {
  357.             $dbType                       strtolower($tableColumn['domain_type']);
  358.             $tableColumn['complete_type'] = $tableColumn['domain_complete_type'];
  359.         }
  360.         $type                   $this->_platform->getDoctrineTypeMapping($dbType);
  361.         $type                   $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type);
  362.         $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type);
  363.         switch ($dbType) {
  364.             case 'smallint':
  365.             case 'int2':
  366.                 $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']);
  367.                 $length                 null;
  368.                 break;
  369.             case 'int':
  370.             case 'int4':
  371.             case 'integer':
  372.                 $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']);
  373.                 $length                 null;
  374.                 break;
  375.             case 'bigint':
  376.             case 'int8':
  377.                 $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']);
  378.                 $length                 null;
  379.                 break;
  380.             case 'bool':
  381.             case 'boolean':
  382.                 if ($tableColumn['default'] === 'true') {
  383.                     $tableColumn['default'] = true;
  384.                 }
  385.                 if ($tableColumn['default'] === 'false') {
  386.                     $tableColumn['default'] = false;
  387.                 }
  388.                 $length null;
  389.                 break;
  390.             case 'text':
  391.             case '_varchar':
  392.             case 'varchar':
  393.                 $tableColumn['default'] = $this->parseDefaultExpression($tableColumn['default']);
  394.                 $fixed                  false;
  395.                 break;
  396.             case 'interval':
  397.                 $fixed false;
  398.                 break;
  399.             case 'char':
  400.             case 'bpchar':
  401.                 $fixed true;
  402.                 break;
  403.             case 'float':
  404.             case 'float4':
  405.             case 'float8':
  406.             case 'double':
  407.             case 'double precision':
  408.             case 'real':
  409.             case 'decimal':
  410.             case 'money':
  411.             case 'numeric':
  412.                 $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']);
  413.                 if (
  414.                     preg_match(
  415.                         '([A-Za-z]+\(([0-9]+),([0-9]+)\))',
  416.                         $tableColumn['complete_type'],
  417.                         $match
  418.                     ) === 1
  419.                 ) {
  420.                     $precision $match[1];
  421.                     $scale     $match[2];
  422.                     $length    null;
  423.                 }
  424.                 break;
  425.             case 'year':
  426.                 $length null;
  427.                 break;
  428.             // PostgreSQL 9.4+ only
  429.             case 'jsonb':
  430.                 $jsonb true;
  431.                 break;
  432.         }
  433.         if (
  434.             $tableColumn['default'] !== null && preg_match(
  435.                 "('([^']+)'::)",
  436.                 $tableColumn['default'],
  437.                 $match
  438.             ) === 1
  439.         ) {
  440.             $tableColumn['default'] = $match[1];
  441.         }
  442.         $options = [
  443.             'length'        => $length,
  444.             'notnull'       => (bool) $tableColumn['isnotnull'],
  445.             'default'       => $tableColumn['default'],
  446.             'precision'     => $precision,
  447.             'scale'         => $scale,
  448.             'fixed'         => $fixed,
  449.             'unsigned'      => false,
  450.             'autoincrement' => $autoincrement,
  451.             'comment'       => isset($tableColumn['comment']) && $tableColumn['comment'] !== ''
  452.                 $tableColumn['comment']
  453.                 : null,
  454.         ];
  455.         $column = new Column($tableColumn['field'], Type::getType($type), $options);
  456.         if (isset($tableColumn['collation']) && ! empty($tableColumn['collation'])) {
  457.             $column->setPlatformOption('collation'$tableColumn['collation']);
  458.         }
  459.         if ($column->getType()->getName() === Types::JSON) {
  460.             if (! $column->getType() instanceof JsonType) {
  461.                 Deprecation::trigger(
  462.                     'doctrine/dbal',
  463.                     'https://github.com/doctrine/dbal/pull/5049',
  464.                     <<<'DEPRECATION'
  465.                     %s not extending %s while being named %s is deprecated,
  466.                     and will lead to jsonb never to being used in 4.0.,
  467.                     DEPRECATION,
  468.                     get_class($column->getType()),
  469.                     JsonType::class,
  470.                     Types::JSON
  471.                 );
  472.             }
  473.             $column->setPlatformOption('jsonb'$jsonb);
  474.         }
  475.         return $column;
  476.     }
  477.     /**
  478.      * PostgreSQL 9.4 puts parentheses around negative numeric default values that need to be stripped eventually.
  479.      *
  480.      * @param mixed $defaultValue
  481.      *
  482.      * @return mixed
  483.      */
  484.     private function fixVersion94NegativeNumericDefaultValue($defaultValue)
  485.     {
  486.         if ($defaultValue !== null && strpos($defaultValue'(') === 0) {
  487.             return trim($defaultValue'()');
  488.         }
  489.         return $defaultValue;
  490.     }
  491.     /**
  492.      * Parses a default value expression as given by PostgreSQL
  493.      */
  494.     private function parseDefaultExpression(?string $default): ?string
  495.     {
  496.         if ($default === null) {
  497.             return $default;
  498.         }
  499.         return str_replace("''""'"$default);
  500.     }
  501.     protected function selectTableNames(string $databaseName): Result
  502.     {
  503.         $sql = <<<'SQL'
  504. SELECT quote_ident(table_name) AS table_name,
  505.        table_schema AS schema_name
  506. FROM information_schema.tables
  507. WHERE table_catalog = ?
  508.   AND table_schema NOT LIKE 'pg\_%'
  509.   AND table_schema != 'information_schema'
  510.   AND table_name != 'geometry_columns'
  511.   AND table_name != 'spatial_ref_sys'
  512.   AND table_type = 'BASE TABLE'
  513. SQL;
  514.         return $this->_conn->executeQuery($sql, [$databaseName]);
  515.     }
  516.     protected function selectTableColumns(string $databaseName, ?string $tableName null): Result
  517.     {
  518.         $sql 'SELECT';
  519.         if ($tableName === null) {
  520.             $sql .= ' c.relname AS table_name, n.nspname AS schema_name,';
  521.         }
  522.         $sql .= <<<'SQL'
  523.             a.attnum,
  524.             quote_ident(a.attname) AS field,
  525.             t.typname AS type,
  526.             format_type(a.atttypid, a.atttypmod) AS complete_type,
  527.             (SELECT tc.collcollate FROM pg_catalog.pg_collation tc WHERE tc.oid = a.attcollation) AS collation,
  528.             (SELECT t1.typname FROM pg_catalog.pg_type t1 WHERE t1.oid = t.typbasetype) AS domain_type,
  529.             (SELECT format_type(t2.typbasetype, t2.typtypmod) FROM
  530.               pg_catalog.pg_type t2 WHERE t2.typtype = 'd' AND t2.oid = a.atttypid) AS domain_complete_type,
  531.             a.attnotnull AS isnotnull,
  532.             (SELECT 't'
  533.              FROM pg_index
  534.              WHERE c.oid = pg_index.indrelid
  535.                 AND pg_index.indkey[0] = a.attnum
  536.                 AND pg_index.indisprimary = 't'
  537.             ) AS pri,
  538.             (SELECT pg_get_expr(adbin, adrelid)
  539.              FROM pg_attrdef
  540.              WHERE c.oid = pg_attrdef.adrelid
  541.                 AND pg_attrdef.adnum=a.attnum
  542.             ) AS default,
  543.             (SELECT pg_description.description
  544.                 FROM pg_description WHERE pg_description.objoid = c.oid AND a.attnum = pg_description.objsubid
  545.             ) AS comment
  546.             FROM pg_attribute a, pg_class c, pg_type t, pg_namespace n
  547. SQL;
  548.         $conditions array_merge([
  549.             'a.attnum > 0',
  550.             'a.attrelid = c.oid',
  551.             'a.atttypid = t.oid',
  552.             'n.oid = c.relnamespace',
  553.             "c.relkind = 'r'",
  554.         ], $this->buildQueryConditions($tableName));
  555.         $sql .= ' WHERE ' implode(' AND '$conditions) . ' ORDER BY a.attnum';
  556.         return $this->_conn->executeQuery($sql);
  557.     }
  558.     protected function selectIndexColumns(string $databaseName, ?string $tableName null): Result
  559.     {
  560.         $sql 'SELECT';
  561.         if ($tableName === null) {
  562.             $sql .= ' tc.relname AS table_name, tn.nspname AS schema_name,';
  563.         }
  564.         $sql .= <<<'SQL'
  565.                    quote_ident(ic.relname) AS relname,
  566.                    i.indisunique,
  567.                    i.indisprimary,
  568.                    i.indkey,
  569.                    i.indrelid,
  570.                    pg_get_expr(indpred, indrelid) AS "where"
  571.               FROM pg_index i
  572.                    JOIN pg_class AS tc ON tc.oid = i.indrelid
  573.                    JOIN pg_namespace tn ON tn.oid = tc.relnamespace
  574.                    JOIN pg_class AS ic ON ic.oid = i.indexrelid
  575.              WHERE ic.oid IN (
  576.                 SELECT indexrelid
  577.                 FROM pg_index i, pg_class c, pg_namespace n
  578. SQL;
  579.         $conditions array_merge([
  580.             'c.oid = i.indrelid',
  581.             'c.relnamespace = n.oid',
  582.         ], $this->buildQueryConditions($tableName));
  583.         $sql .= ' WHERE ' implode(' AND '$conditions) . ')';
  584.         return $this->_conn->executeQuery($sql);
  585.     }
  586.     protected function selectForeignKeyColumns(string $databaseName, ?string $tableName null): Result
  587.     {
  588.         $sql 'SELECT';
  589.         if ($tableName === null) {
  590.             $sql .= ' tc.relname AS table_name, tn.nspname AS schema_name,';
  591.         }
  592.         $sql .= <<<'SQL'
  593.                   quote_ident(r.conname) as conname,
  594.                   pg_get_constraintdef(r.oid, true) as condef
  595.                   FROM pg_constraint r
  596.                       JOIN pg_class AS tc ON tc.oid = r.conrelid
  597.                       JOIN pg_namespace tn ON tn.oid = tc.relnamespace
  598.                   WHERE r.conrelid IN
  599.                   (
  600.                       SELECT c.oid
  601.                       FROM pg_class c, pg_namespace n
  602. SQL;
  603.         $conditions array_merge(['n.oid = c.relnamespace'], $this->buildQueryConditions($tableName));
  604.         $sql .= ' WHERE ' implode(' AND '$conditions) . ") AND r.contype = 'f'";
  605.         return $this->_conn->executeQuery($sql);
  606.     }
  607.     /**
  608.      * {@inheritDoc}
  609.      */
  610.     protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName null): array
  611.     {
  612.         $sql = <<<'SQL'
  613. SELECT c.relname,
  614.        obj_description(c.oid, 'pg_class') AS comment
  615. FROM pg_class c
  616.      INNER JOIN pg_namespace n
  617.          ON n.oid = c.relnamespace
  618. SQL;
  619.         $conditions array_merge(["c.relkind = 'r'"], $this->buildQueryConditions($tableName));
  620.         $sql .= ' WHERE ' implode(' AND '$conditions);
  621.         return $this->_conn->fetchAllAssociativeIndexed($sql);
  622.     }
  623.     /**
  624.      * @param string|null $tableName
  625.      *
  626.      * @return list<string>
  627.      */
  628.     private function buildQueryConditions($tableName): array
  629.     {
  630.         $conditions = [];
  631.         $schemaName null;
  632.         if ($tableName !== null) {
  633.             if (strpos($tableName'.') !== false) {
  634.                 [$schemaName$tableName] = explode('.'$tableName);
  635.             }
  636.             $identifier   = new Identifier($tableName);
  637.             $conditions[] = 'c.relname = ' $this->_platform->quoteStringLiteral($identifier->getName());
  638.         }
  639.         if ($schemaName !== null) {
  640.             $conditions[] = 'n.nspname = ' $this->_platform->quoteStringLiteral($schemaName);
  641.         } else {
  642.             $conditions[] = "n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')";
  643.         }
  644.         return $conditions;
  645.     }
  646. }