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

dg / dibi / 22284747548

22 Feb 2026 08:29PM UTC coverage: 76.399% (+0.09%) from 76.31%
22284747548

push

github

dg
added CLAUDE.md

1761 of 2305 relevant lines covered (76.4%)

0.76 hits per line

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

63.38
/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
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
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
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✔
151
                ") ?? throw new \LogicException('Unexpected null result.');
×
152

153
                $indexes = [];
1✔
154
                while ($row = $res->fetch(true)) {
1✔
155
                        $indexes[$row['relname']]['name'] = $row['relname'];
1✔
156
                        $indexes[$row['relname']]['unique'] = $row['indisunique'] === 't' || $row['indisunique'] === true;
1✔
157
                        $indexes[$row['relname']]['primary'] = $row['indisprimary'] === 't' || $row['indisprimary'] === true;
1✔
158
                        $indexes[$row['relname']]['columns'] = [];
1✔
159
                        foreach (explode(' ', $row['indkey']) as $index) {
1✔
160
                                if (isset($columns[$index])) {
1✔
161
                                        $indexes[$row['relname']]['columns'][] = $columns[$index];
1✔
162
                                }
163
                        }
164
                }
165

166
                return array_values($indexes);
1✔
167
        }
168

169

170
        /**
171
         * Returns metadata for all foreign keys in a table.
172
         */
173
        public function getForeignKeys(string $table): array
174
        {
175
                $_table = $this->driver->escapeText($this->driver->escapeIdentifier($table));
×
176

177
                $res = $this->driver->query("
×
178
                        SELECT
179
                                c.conname AS name,
180
                                lt.attname AS local,
181
                                c.confrelid::regclass AS table,
182
                                ft.attname AS foreign,
183

184
                                CASE c.confupdtype
185
                                        WHEN 'a' THEN 'NO ACTION'
186
                                        WHEN 'r' THEN 'RESTRICT'
187
                                        WHEN 'c' THEN 'CASCADE'
188
                                        WHEN 'n' THEN 'SET NULL'
189
                                        WHEN 'd' THEN 'SET DEFAULT'
190
                                        ELSE 'UNKNOWN'
191
                                END AS \"onUpdate\",
192

193
                                CASE c.confdeltype
194
                                        WHEN 'a' THEN 'NO ACTION'
195
                                        WHEN 'r' THEN 'RESTRICT'
196
                                        WHEN 'c' THEN 'CASCADE'
197
                                        WHEN 'n' THEN 'SET NULL'
198
                                        WHEN 'd' THEN 'SET DEFAULT'
199
                                        ELSE 'UNKNOWN'
200
                                END AS \"onDelete\",
201

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

216
                $fKeys = $references = [];
×
217
                while ($row = $res->fetch(true)) {
×
218
                        if (!isset($fKeys[$row['name']])) {
×
219
                                $fKeys[$row['name']] = [
×
220
                                        'name' => $row['name'],
×
221
                                        'table' => $row['table'],
×
222
                                        'local' => [],
223
                                        'foreign' => [],
224
                                        'onUpdate' => $row['onUpdate'],
×
225
                                        'onDelete' => $row['onDelete'],
×
226
                                ];
227

228
                                $l = explode(',', trim($row['conkey'], '{}'));
×
229
                                $f = explode(',', trim($row['confkey'], '{}'));
×
230

231
                                $references[$row['name']] = array_combine($l, $f);
×
232
                        }
233

234
                        if (
235
                                isset($references[$row['name']][$row['lnum']])
×
236
                                && $references[$row['name']][$row['lnum']] === $row['fnum']
×
237
                        ) {
238
                                $fKeys[$row['name']]['local'][] = $row['local'];
×
239
                                $fKeys[$row['name']]['foreign'][] = $row['foreign'];
×
240
                        }
241
                }
242

243
                return $fKeys;
×
244
        }
245
}
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