Passed
Push — main ( f81a94...bb3422 )
by Thierry
07:07
created

Database::getQueries()   C

Complexity

Conditions 12
Paths 56

Size

Total Lines 31
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 12
eloc 21
c 1
b 0
f 0
nc 56
nop 1
dl 0
loc 31
rs 6.9666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Lagdo\DbAdmin\Driver\PgSql\Db;
4
5
use Lagdo\DbAdmin\Driver\Entity\TableEntity;
6
use Lagdo\DbAdmin\Driver\Entity\RoutineEntity;
7
8
use Lagdo\DbAdmin\Driver\Db\Database as AbstractDatabase;
9
10
class Database extends AbstractDatabase
11
{
12
    /**
13
     * Get queries to create or alter table.
14
     *
15
     * @param TableEntity $tableAttrs
16
     *
17
     * @return array
18
     */
19
    private function getQueries(TableEntity $tableAttrs)
20
    {
21
        $queries = [];
22
23
        foreach ($tableAttrs->edited as $field) {
24
            $column = $this->driver->escapeId($field[0]);
25
            $val = $field[1];
26
            $val5 = $val[5] ?? '';
27
            if ($val[0] !== '' && $column != $val[0]) {
28
                $queries[] = 'ALTER TABLE ' . $this->driver->table($tableAttrs->name) . " RENAME $column TO $val[0]";
29
            }
30
            if ($column !== '' || $val5 !== '') {
31
                $queries[] = 'COMMENT ON COLUMN ' . $this->driver->table($tableAttrs->name) .
32
                    ".$val[0] IS " . ($val5 != '' ? substr($val5, 9) : "''");
33
            }
34
        }
35
        foreach ($tableAttrs->fields as $field) {
36
            $column = $this->driver->escapeId($field[0]);
37
            $val = $field[1];
38
            $val5 = $val[5] ?? '';
39
            if ($column !== '' || $val5 !== '') {
40
                $queries[] = 'COMMENT ON COLUMN ' . $this->driver->table($tableAttrs->name) .
41
                    ".$val[0] IS " . ($val5 != '' ? substr($val5, 9) : "''");
42
            }
43
        }
44
        if ($tableAttrs->comment != '') {
45
            $queries[] = 'COMMENT ON TABLE ' . $this->driver->table($tableAttrs->name) .
46
                ' IS ' . $this->driver->quote($tableAttrs->comment);
0 ignored issues
show
Bug introduced by
The method quote() does not exist on Lagdo\DbAdmin\Driver\DriverInterface. Did you maybe mean quoteBinary()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

46
                ' IS ' . $this->driver->/** @scrutinizer ignore-call */ quote($tableAttrs->comment);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
47
        }
48
49
        return $queries;
50
    }
51
52
    /**
53
     * Get queries to create or alter table.
54
     *
55
     * @param TableEntity $tableAttrs
56
     *
57
     * @return array
58
     */
59
    private function getNewColumns(TableEntity $tableAttrs)
60
    {
61
        $columns = [];
62
63
        foreach ($tableAttrs->fields as $field) {
64
            $column = $this->driver->escapeId($field[0]);
0 ignored issues
show
Unused Code introduced by
The assignment to $column is dead and can be removed.
Loading history...
65
            $val = $field[1];
66
            if (isset($val[6])) { // auto increment
67
                $val[1] = ($val[1] == ' bigint' ? ' big' : ($val[1] == ' smallint' ? ' small' : ' ')) . 'serial';
68
            }
69
            $columns[] = implode($val);
70
            if (isset($val[6])) {
71
                $columns[] = " PRIMARY KEY ($val[0])";
72
            }
73
        }
74
75
        return $columns;
76
    }
77
78
    /**
79
     * Get queries to create or alter table.
80
     *
81
     * @param TableEntity $tableAttrs
82
     *
83
     * @return array
84
     */
85
    private function getColumnChanges(TableEntity $tableAttrs)
86
    {
87
        $columns = [];
88
89
        foreach ($tableAttrs->fields as $field) {
90
            $column = $this->driver->escapeId($field[0]);
0 ignored issues
show
Unused Code introduced by
The assignment to $column is dead and can be removed.
Loading history...
91
            $val = $field[1];
92
            if (isset($val[6])) { // auto increment
93
                $val[1] = ($val[1] == ' bigint' ? ' big' : ($val[1] == ' smallint' ? ' small' : ' ')) . 'serial';
94
            }
95
            $columns[] = 'ADD ' . implode($val);
96
            if (isset($val[6])) {
97
                $columns[] = "ADD PRIMARY KEY ($val[0])";
98
            }
99
        }
100
        foreach ($tableAttrs->edited as $field) {
101
            $column = $this->driver->escapeId($field[0]);
102
            $val = $field[1];
103
            $columns[] = "ALTER $column TYPE$val[1]";
104
            if (!$val[6]) {
105
                $columns[] = "ALTER $column " . ($val[3] ? "SET$val[3]" : 'DROP DEFAULT');
106
                $columns[] = "ALTER $column " . ($val[2] == ' NULL' ? 'DROP NOT' : 'SET') . $val[2];
107
            }
108
        }
109
        foreach ($tableAttrs->dropped as $column) {
110
            $columns[] = 'DROP ' . $this->driver->execute($column);
0 ignored issues
show
Bug introduced by
Are you sure $this->driver->execute($column) of type Lagdo\DbAdmin\Driver\Db\StatementInterface|boolean can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

110
            $columns[] = 'DROP ' . /** @scrutinizer ignore-type */ $this->driver->execute($column);
Loading history...
111
        }
112
113
        return $columns;
114
    }
115
116
    /**
117
     * @inheritDoc
118
     */
119
    public function createTable(TableEntity $tableAttrs)
120
    {
121
        $queries = $this->getQueries($tableAttrs);
122
        $columns = $this->getNewColumns($tableAttrs);
123
        $columns = array_merge($columns, $tableAttrs->foreign);
124
        array_unshift($queries, 'CREATE TABLE ' . $this->driver->table($tableAttrs->name) .
125
            '(' . implode(', ', $columns) . ')');
126
        foreach ($queries as $query) {
127
            $this->driver->execute($query);
128
        }
129
        return true;
130
    }
131
132
    /**
133
     * @inheritDoc
134
     */
135
    public function alterTable(string $table, TableEntity $tableAttrs)
136
    {
137
        $queries = $this->getQueries($tableAttrs);
138
        $columns = $this->getColumnChanges($tableAttrs);
139
        if ($tableAttrs->name !== '' && $table !== $tableAttrs->name) {
140
            array_unshift($queries, 'ALTER TABLE ' . $this->driver->table($table) .
141
                ' RENAME TO ' . $this->driver->table($tableAttrs->name));
142
        }
143
        $columns = array_merge($columns, $tableAttrs->foreign);
144
        if (!empty($columns)) {
145
            array_unshift($queries, 'ALTER TABLE ' . $this->driver->table($table) . ' ' . implode(', ', $columns));
146
        }
147
        // if ($tableAttrs->autoIncrement != '') {
148
        //     //! $queries[] = 'SELECT setval(pg_get_serial_sequence(' . $this->driver->quote($tableAttrs->name) . ', ), $tableAttrs->autoIncrement)';
149
        // }
150
        foreach ($queries as $query) {
151
            $this->driver->execute($query);
152
        }
153
        return true;
154
    }
155
156
    /**
157
     * @inheritDoc
158
     */
159
    public function alterIndexes(string $table, array $alter, array $drop)
160
    {
161
        $queries = [];
162
        $columns = [];
163
        foreach (array_reverse($drop) as $index) {
164
            if ($index->type === 'INDEX') {
165
                $queries[] = 'DROP INDEX ' . $this->driver->escapeId($index);
166
            } else {
167
                $columns[] = 'DROP CONSTRAINT ' . $this->driver->escapeId($index->name);
168
            }
169
        }
170
        foreach ($alter as $index) {
171
            if ($index->type === 'INDEX') {
172
                $queries[] = 'CREATE INDEX ' .
173
                    $this->driver->escapeId($index->name != '' ? $index->name : uniqid($table . '_')) .
174
                    ' ON ' . $this->driver->table($table) . ' (' . implode(', ', $index->columns) . ')';
175
            } else {
176
                //! descending UNIQUE indexes results in syntax error
177
                $constraint = ($index->name != '' ? ' CONSTRAINT ' . $this->driver->escapeId($index->name) : '');
178
                $columns[] = "ADD$constraint " . ($index->type == 'PRIMARY' ? 'PRIMARY KEY' : $index->type) .
179
                    ' (' . implode(', ', $index->columns) . ')';
180
            }
181
        }
182
        if (!empty($columns)) {
183
            array_unshift($queries, 'ALTER TABLE ' . $this->driver->table($table) . implode(', ', $columns));
184
        }
185
        foreach ($queries as $query) {
186
            $this->driver->execute($query);
187
        }
188
        return true;
189
    }
190
191
    /**
192
     * @inheritDoc
193
     */
194
    public function tables()
195
    {
196
        $query = 'SELECT table_name, table_type FROM information_schema.tables WHERE table_schema = current_schema()';
197
        if ($this->driver->support('materializedview')) {
198
            $query .= " UNION ALL SELECT matviewname, 'MATERIALIZED VIEW' FROM pg_matviews WHERE schemaname = current_schema()";
199
        }
200
        $query .= ' ORDER BY 1';
201
        return $this->driver->keyValues($query);
202
    }
203
204
    /**
205
     * @inheritDoc
206
     */
207
    public function sequences()
208
    {
209
        // From db.inc.php
210
        $query = 'SELECT sequence_name FROM information_schema.sequences ' .
211
            'WHERE sequence_schema = selectedSchema() ORDER BY sequence_name';
212
        return $this->driver->values($query);
213
    }
214
215
    /**
216
     * @inheritDoc
217
     */
218
    public function countTables(array $databases)
219
    {
220
        $connection = $this->driver->createConnection(); // New connection
221
        $counts = [];
222
        $systemSchemas = ['information_schema', 'pg_catalog', 'pg_temp_1', 'pg_toast', 'pg_toast_temp_1'];
223
        $query = "SELECT count(*) FROM information_schema.tables WHERE table_schema NOT IN ('" .
224
            implode("','", $systemSchemas) . "')";
225
        foreach ($databases as $database) {
226
            $counts[$database] = 0;
227
            if (!$connection->open($database)) {
228
                continue;
229
            }
230
            $statement = $connection->query($query);
231
            if (is_object($statement) && ($row = $statement->fetchRow())) {
232
                $counts[$database] = intval($row[0]);
233
            }
234
        }
235
        return $counts;
236
    }
237
238
    /**
239
     * @inheritDoc
240
     */
241
    public function dropViews(array $views)
242
    {
243
        return $this->dropTables($views);
244
    }
245
246
    /**
247
     * @inheritDoc
248
     */
249
    public function dropTables(array $tables)
250
    {
251
        foreach ($tables as $table) {
252
            $status = $this->driver->tableStatus($table);
253
            if (!$this->driver->execute('DROP ' . strtoupper($status->engine) . ' ' . $this->driver->table($table))) {
254
                return false;
255
            }
256
        }
257
        return true;
258
    }
259
260
    /**
261
     * @inheritDoc
262
     */
263
    public function moveTables(array $tables, array $views, string $target)
264
    {
265
        foreach (array_merge($tables, $views) as $table) {
266
            $status = $this->driver->tableStatus($table);
267
            if (!$this->driver->execute('ALTER ' . strtoupper($status->engine) . ' ' .
268
                $this->driver->table($table) . ' SET SCHEMA ' . $this->driver->escapeId($target))) {
269
                return false;
270
            }
271
        }
272
        return true;
273
    }
274
275
    /**
276
     * @inheritDoc
277
     */
278
    public function truncateTables(array $tables)
279
    {
280
        $this->driver->execute('TRUNCATE ' . implode(', ', array_map(function ($table) {
281
            return $this->driver->table($table);
282
        }, $tables)));
283
        return true;
284
    }
285
286
    /**
287
     * @inheritDoc
288
     */
289
    public function userTypes()
290
    {
291
        $query = 'SELECT typname FROM pg_type WHERE typnamespace = ' .
292
            '(SELECT oid FROM pg_namespace WHERE nspname = current_schema()) ' .
293
            "AND typtype IN ('b','d','e') AND typelem = 0";
294
        return $this->driver->values($query);
295
    }
296
297
    /**
298
     * @inheritDoc
299
     */
300
    public function schemas()
301
    {
302
        return $this->driver->values('SELECT nspname FROM pg_namespace ORDER BY nspname');
303
    }
304
305
    /**
306
     * @inheritDoc
307
     */
308
    public function routine(string $name, string $type)
309
    {
310
        $query = 'SELECT routine_definition AS definition, LOWER(external_language) AS language, * ' .
311
            'FROM information_schema.routines WHERE routine_schema = current_schema() ' .
312
            'AND specific_name = ' . $this->driver->quote($name);
313
        $rows = $this->driver->rows($query);
314
        $routines = $rows[0];
315
        $routines['returns'] = ['type' => $routines['type_udt_name']];
316
        $query = 'SELECT parameter_name AS field, data_type AS type, character_maximum_length AS length, ' .
317
            'parameter_mode AS inout FROM information_schema.parameters WHERE specific_schema = current_schema() ' .
318
            'AND specific_name = ' . $this->driver->quote($name) . ' ORDER BY ordinal_position';
319
        $routines['fields'] = $this->driver->rows($query);
320
        return $routines;
321
    }
322
323
    /**
324
     * @inheritDoc
325
     */
326
    public function routines()
327
    {
328
        $query = 'SELECT specific_name AS "SPECIFIC_NAME", routine_type AS "ROUTINE_TYPE", ' .
329
            'routine_name AS "ROUTINE_NAME", type_udt_name AS "DTD_IDENTIFIER" ' .
330
            'FROM information_schema.routines WHERE routine_schema = current_schema() ORDER BY SPECIFIC_NAME';
331
        $rows = $this->driver->rows($query);
332
        return array_map(function($row) {
333
            return new RoutineEntity($row['ROUTINE_NAME'], $row['SPECIFIC_NAME'], $row['ROUTINE_TYPE'], $row['DTD_IDENTIFIER']);
334
        }, $rows);
335
    }
336
337
    /**
338
     * @inheritDoc
339
     */
340
    public function routineId(string $name, array $row)
341
    {
342
        $routine = [];
343
        foreach ($row['fields'] as $field) {
344
            $routine[] = $field->type;
345
        }
346
        return $this->driver->escapeId($name) . '(' . implode(', ', $routine) . ')';
347
    }
348
}
349