• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dg / dibi / 22282875037

22 Feb 2026 06:32PM UTC coverage: 76.552% (-0.5%) from 77.004%
22282875037

push

github

dg
phpstan

55 of 96 new or added lines in 23 files covered. (57.29%)

1 existing line in 1 file now uncovered.

1763 of 2303 relevant lines covered (76.55%)

0.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.86
/src/Dibi/Drivers/PostgreReflector.php
1
<?php declare(strict_types=1);
2

3
/**
4
 * This file is part of the Dibi, smart database abstraction layer (https://dibi.nette.org)
5
 * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
6
 */
7

8
namespace Dibi\Drivers;
9

10
use Dibi;
11

12

13
/**
14
 * The reflector for PostgreSQL database.
15
 */
16
class PostgreReflector implements Dibi\Reflector
17
{
18
        public function __construct(
1✔
19
                private readonly Dibi\Driver $driver,
20
        ) {
21
        }
1✔
22

23

24
        /**
25
         * Returns list of tables.
26
         */
27
        public function getTables(): array
28
        {
29
                $query = "
1✔
30
                        SELECT
31
                                table_name AS name,
32
                                CASE table_type
33
                                        WHEN 'VIEW' THEN 1
34
                                        ELSE 0
35
                                END AS view
36
                        FROM
37
                                information_schema.tables
38
                        WHERE
39
                                table_schema = ANY (current_schemas(false))
40

41
                        UNION ALL
42
                        SELECT
43
                                matviewname, 1
44
                        FROM
45
                                pg_matviews
46
                        WHERE
47
                                schemaname = ANY (current_schemas(false))";
48

49
                $res = $this->driver->query($query) ?? throw new \LogicException('Unexpected null result.');
1✔
50
                $tables = [];
1✔
51
                while ($row = $res->fetch(true)) {
1✔
52
                        $tables[] = $row;
1✔
53
                }
54

55
                /** @var list<array{name: string, view: bool}> */
56
                return $tables;
1✔
57
        }
58

59

60
        /**
61
         * Returns metadata for all columns in a table.
62
         */
63
        public function getColumns(string $table): array
1✔
64
        {
65
                $_table = $this->driver->escapeText($this->driver->escapeIdentifier($table));
1✔
66

67
                $res = $this->driver->query("
1✔
68
                        SELECT *
69
                        FROM information_schema.columns c
70
                        JOIN pg_class ON pg_class.relname = c.table_name
71
                        JOIN pg_namespace nsp ON nsp.oid = pg_class.relnamespace AND nsp.nspname = c.table_schema
72
                        WHERE pg_class.oid = $_table::regclass
1✔
73
                        ORDER BY c.ordinal_position
NEW
74
                ") ?? throw new \LogicException('Unexpected null result.');
×
75

76
                if (!$res->getRowCount()) {
1✔
77
                        $res = $this->driver->query("
×
78
                                SELECT
79
                                        a.attname AS column_name,
80
                                        pg_type.typname AS udt_name,
81
                                        a.attlen AS numeric_precision,
82
                                        a.atttypmod-4 AS character_maximum_length,
83
                                        NOT a.attnotnull AS is_nullable,
84
                                        a.attnum AS ordinal_position,
85
                                        pg_get_expr(adef.adbin, adef.adrelid) AS column_default,
86
                                        CASE WHEN a.attidentity IN ('a', 'd') THEN 'YES' ELSE 'NO' END AS is_identity
87
                                FROM
88
                                        pg_attribute a
89
                                        JOIN pg_type ON a.atttypid = pg_type.oid
90
                                        JOIN pg_class cls ON a.attrelid = cls.oid
91
                                        LEFT JOIN pg_attrdef adef ON adef.adnum = a.attnum AND adef.adrelid = a.attrelid
92
                                WHERE
93
                                        cls.relkind IN ('r', 'v', 'mv')
94
                                        AND a.attrelid = $_table::regclass
×
95
                                        AND a.attnum > 0
96
                                        AND NOT a.attisdropped
97
                                ORDER BY ordinal_position
NEW
98
                        ") ?? throw new \LogicException('Unexpected null result.');
×
99
                }
100

101
                $columns = [];
1✔
102
                while ($row = $res->fetch(true)) {
1✔
103
                        $size = (int) max($row['character_maximum_length'], $row['numeric_precision']);
1✔
104
                        $columns[] = [
1✔
105
                                'name' => $row['column_name'],
1✔
106
                                'table' => $table,
1✔
107
                                'nativetype' => strtoupper($row['udt_name']),
1✔
108
                                'size' => $size > 0 ? $size : null,
1✔
109
                                'nullable' => $row['is_nullable'] === 'YES' || $row['is_nullable'] === 't' || $row['is_nullable'] === true,
1✔
110
                                'default' => $row['column_default'],
1✔
111
                                'autoincrement' => $row['is_identity'] === 'YES' || str_starts_with($row['column_default'] ?? '', 'nextval('),
1✔
112
                                'vendor' => $row,
1✔
113
                        ];
114
                }
115

116
                return $columns;
1✔
117
        }
118

119

120
        /**
121
         * Returns metadata for all indexes in a table.
122
         */
123
        public function getIndexes(string $table): array
1✔
124
        {
125
                $_table = $this->driver->escapeText($this->driver->escapeIdentifier($table));
1✔
126
                $res = $this->driver->query("
1✔
127
                        SELECT
128
                                a.attnum AS ordinal_position,
129
                                a.attname AS column_name
130
                        FROM
131
                                pg_attribute a
132
                                JOIN pg_class cls ON a.attrelid = cls.oid
133
                        WHERE
134
                                a.attrelid = $_table::regclass
1✔
135
                                AND a.attnum > 0
136
                                AND NOT a.attisdropped
137
                        ORDER BY ordinal_position
NEW
138
                ") ?? throw new \LogicException('Unexpected null result.');
×
139

140
                $columns = [];
1✔
141
                while ($row = $res->fetch(true)) {
1✔
142
                        $columns[$row['ordinal_position']] = $row['column_name'];
1✔
143
                }
144

145
                $res = $this->driver->query("
1✔
146
                        SELECT pg_class2.relname, indisunique, indisprimary, indkey
147
                        FROM pg_class
148
                        LEFT JOIN pg_index on pg_class.oid = pg_index.indrelid
149
                        INNER JOIN pg_class as pg_class2 on pg_class2.oid = pg_index.indexrelid
150
                        WHERE pg_class.oid = $_table::regclass
1✔
NEW
151
                ") ?? throw new \LogicException('Unexpected null result.');
×
152

153
                $indexes = [];
1✔
154
                while ($row = $res->fetch(true)) {
1✔
155
                        $name = (string) $row['relname'];
1✔
156
                        $cols = [];
1✔
157
                        foreach (explode(' ', (string) $row['indkey']) as $index) {
1✔
158
                                if (isset($columns[$index])) {
1✔
159
                                        $cols[] = $columns[$index];
1✔
160
                                }
161
                        }
162

163
                        $indexes[$name] = [
1✔
164
                                'name' => $name,
1✔
165
                                'unique' => $row['indisunique'] === 't' || $row['indisunique'] === true,
1✔
166
                                'primary' => $row['indisprimary'] === 't' || $row['indisprimary'] === true,
1✔
167
                                'columns' => $cols,
1✔
168
                        ];
169
                }
170

171
                return array_values($indexes);
1✔
172
        }
173

174

175
        /**
176
         * Returns metadata for all foreign keys in a table.
177
         */
178
        public function getForeignKeys(string $table): array
179
        {
180
                $_table = $this->driver->escapeText($this->driver->escapeIdentifier($table));
×
181

182
                $res = $this->driver->query("
×
183
                        SELECT
184
                                c.conname AS name,
185
                                lt.attname AS local,
186
                                c.confrelid::regclass AS table,
187
                                ft.attname AS foreign,
188

189
                                CASE c.confupdtype
190
                                        WHEN 'a' THEN 'NO ACTION'
191
                                        WHEN 'r' THEN 'RESTRICT'
192
                                        WHEN 'c' THEN 'CASCADE'
193
                                        WHEN 'n' THEN 'SET NULL'
194
                                        WHEN 'd' THEN 'SET DEFAULT'
195
                                        ELSE 'UNKNOWN'
196
                                END AS \"onUpdate\",
197

198
                                CASE c.confdeltype
199
                                        WHEN 'a' THEN 'NO ACTION'
200
                                        WHEN 'r' THEN 'RESTRICT'
201
                                        WHEN 'c' THEN 'CASCADE'
202
                                        WHEN 'n' THEN 'SET NULL'
203
                                        WHEN 'd' THEN 'SET DEFAULT'
204
                                        ELSE 'UNKNOWN'
205
                                END AS \"onDelete\",
206

207
                                c.conkey,
208
                                lt.attnum AS lnum,
209
                                c.confkey,
210
                                ft.attnum AS fnum
211
                        FROM
212
                                pg_constraint c
213
                                JOIN pg_attribute lt ON c.conrelid = lt.attrelid AND lt.attnum = ANY (c.conkey)
214
                                JOIN pg_attribute ft ON c.confrelid = ft.attrelid AND ft.attnum = ANY (c.confkey)
215
                        WHERE
216
                                c.contype = 'f'
217
                                AND
218
                                c.conrelid = $_table::regclass
×
NEW
219
                ") ?? throw new \LogicException('Unexpected null result.');
×
220

221
                $fKeys = $references = [];
×
222
                while ($row = $res->fetch(true)) {
×
223
                        if (!isset($fKeys[$row['name']])) {
×
224
                                $fKeys[$row['name']] = [
×
225
                                        'name' => $row['name'],
×
226
                                        'table' => $row['table'],
×
227
                                        'local' => [],
228
                                        'foreign' => [],
229
                                        'onUpdate' => $row['onUpdate'],
×
230
                                        'onDelete' => $row['onDelete'],
×
231
                                ];
232

233
                                $l = explode(',', trim($row['conkey'], '{}'));
×
234
                                $f = explode(',', trim($row['confkey'], '{}'));
×
235

236
                                $references[$row['name']] = array_combine($l, $f);
×
237
                        }
238

239
                        if (
240
                                isset($references[$row['name']][$row['lnum']])
×
241
                                && $references[$row['name']][$row['lnum']] === $row['fnum']
×
242
                        ) {
243
                                $fKeys[$row['name']]['local'][] = $row['local'];
×
244
                                $fKeys[$row['name']]['foreign'][] = $row['foreign'];
×
245
                        }
246
                }
247

NEW
248
                return array_values($fKeys);
×
249
        }
250
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc