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

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