Passed
Push — 2.x ( 67d9b2...73d5f4 )
by Aleksei
19:21 queued 13s
created

PostgresHandler::getTableNames()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5.025

Importance

Changes 0
Metric Value
cc 5
eloc 11
nc 6
nop 1
dl 0
loc 22
ccs 9
cts 10
cp 0.9
crap 5.025
rs 9.6111
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This file is part of Cycle ORM package.
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace Cycle\Database\Driver\Postgres;
13
14
use Cycle\Database\Driver\Handler;
15
use Cycle\Database\Driver\Postgres\Exception\PostgresException;
16
use Cycle\Database\Driver\Postgres\Schema\PostgresColumn;
17
use Cycle\Database\Driver\Postgres\Schema\PostgresTable;
18
use Cycle\Database\Exception\SchemaException;
19
use Cycle\Database\Schema\AbstractColumn;
0 ignored issues
show
Bug introduced by
The type Cycle\Database\Schema\AbstractColumn was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
20
use Cycle\Database\Schema\AbstractTable;
21
22
/**
23
 * @property PostgresDriver $driver
24
 */
25
class PostgresHandler extends Handler
26
{
27
    /**
28
     * @psalm-param non-empty-string $table
29 522
     */
30
    public function getSchema(string $table, string $prefix = null): AbstractTable
31 522
    {
32
        return new PostgresTable($this->driver, $table, $prefix ?? '');
33
    }
34 920
35
    public function getTableNames(string $prefix = ''): array
36 920
    {
37
        $query = "SELECT table_schema, table_name
38
            FROM information_schema.tables
39
            WHERE table_type = 'BASE TABLE'";
40 920
41 920
        if ($this->driver->shouldUseDefinedSchemas()) {
42
            $query .= " AND table_schema in ('" . implode("','", $this->driver->getSearchSchemas()) . "')";
43
        } else {
44
            $query .= " AND table_schema !~ '^pg_.*' AND table_schema != 'information_schema'";
45
        }
46 920
47 920
        $tables = [];
48 478
        foreach ($this->driver->query($query) as $row) {
49 2
            if ($prefix !== '' && !str_starts_with($row['table_name'], $prefix)) {
50
                continue;
51
            }
52 478
53
            $tables[] = $row['table_schema'] . '.' . $row['table_name'];
54
        }
55 920
56
        return $tables;
57
    }
58
59
    /**
60
     * @psalm-param non-empty-string $table
61 534
     */
62
    public function hasTable(string $table): bool
63 534
    {
64
        [$schema, $name] = $this->driver->parseSchemaAndTable($table);
65 534
66
        $query = "SELECT COUNT(table_name)
67
            FROM information_schema.tables
68
            WHERE table_schema = ?
69
            AND table_type = 'BASE TABLE'
70
            AND table_name = ?";
71 534
72
        return (bool)$this->driver->query($query, [$schema, $name])->fetchColumn();
73
    }
74 12
75
    public function eraseTable(AbstractTable $table, bool $restartIdentity = false): void
76 12
    {
77 12
        $query = "TRUNCATE TABLE {$this->driver->identifier($table->getFullName())}";
78
79 10
        if ($restartIdentity) {
80
            $query .= ' RESTART IDENTITY CASCADE';
81
        }
82
83
        $this->driver->execute($query);
84
    }
85 2
86
    /**
87
     * @psalm-param non-empty-string $table
88 2
     * @psalm-param non-empty-string $name
89
     */
90 2
    public function renameTable(string $table, string $name): void
91 2
    {
92
        // New table name should not contain a schema
93
        [, $name] = $this->driver->parseSchemaAndTable($name);
94
95
        parent::renameTable($table, $name);
96 40
    }
97
98
    /**
99
     * @throws SchemaException
100
     */
101 40
    public function alterColumn(
102
        AbstractTable $table,
103
        AbstractColumn $initial,
104
        AbstractColumn $column
105
    ): void {
106 40
        if (!$initial instanceof PostgresColumn || !$column instanceof PostgresColumn) {
107 18
            throw new SchemaException('Postgres handler can work only with Postgres columns');
108
        }
109
110 18
        //Rename is separate operation
111
        if ($column->getName() !== $initial->getName()) {
112
            $this->renameColumn($table, $initial, $column);
113
114 40
            //This call is required to correctly built set of alter operations
115 40
            $initial->setName($column->getName());
116 12
        }
117
118
        //Postgres columns should be altered using set of operations
119
        $operations = $column->alterOperations($this->driver, $initial);
120 28
        if (empty($operations)) {
121 28
            return;
122 28
        }
123 28
124
        //Postgres columns should be altered using set of operations
125
        $query = sprintf(
126 28
            'ALTER TABLE %s %s',
127 28
            $this->identify($table),
128
            trim(implode(', ', $operations), ', ')
129
        );
130
131
        $this->run($query);
132 516
    }
133
134 516
    public function enableForeignKeyConstraints(): void
135
    {
136 516
        $this->run('SET CONSTRAINTS ALL IMMEDIATE;');
137
    }
138
139 516
    public function disableForeignKeyConstraints(): void
140
    {
141
        $this->run('SET CONSTRAINTS ALL DEFERRED;');
142 18
    }
143
144
    /**
145
     * @psalm-param non-empty-string $statement
146
     */
147 18
    protected function run(string $statement, array $parameters = []): int
148 18
    {
149 18
        if ($this->driver instanceof PostgresDriver) {
0 ignored issues
show
introduced by
$this->driver is always a sub-type of Cycle\Database\Driver\Postgres\PostgresDriver.
Loading history...
150 18
            // invaliding primary key cache
151 18
            $this->driver->resetPrimaryKeys();
152
        }
153
154 18
        return parent::run($statement, $parameters);
155 18
    }
156
157
    /**
158
     * @throws PostgresException
159
     */
160
    protected function assertValid(AbstractColumn $column): void
161
    {
162
        if ($column->getDefaultValue() !== null && \in_array($column->getAbstractType(), ['json', 'jsonb'])) {
163
            try {
164
                \json_decode($column->getDefaultValue(), true, 512, JSON_THROW_ON_ERROR);
165
            } catch (\Throwable) {
166
                throw new PostgresException(
167
                    \sprintf('Column `%s` of type json/jsonb has an invalid default json value.', $column),
168
                );
169
            }
170
        }
171
    }
172
173
    private function renameColumn(
174
        AbstractTable $table,
175
        AbstractColumn $initial,
176
        AbstractColumn $column
177
    ): void {
178
        $statement = sprintf(
179
            'ALTER TABLE %s RENAME COLUMN %s TO %s',
180
            $this->identify($table),
181
            $this->identify($initial),
182
            $this->identify($column)
183
        );
184
185
        $this->run($statement);
186
    }
187
}
188