Subversion-Projekte lars-tiefland.laravel_shop

Revision

Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

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