Issues (90)

src/manipulators/Postgresql.php (6 issues)

1
<?php
2
3
namespace yentu\manipulators;
4
5
use yentu\Parameters;
6
7
class Postgresql extends AbstractDatabaseManipulator
8
{
9
10
    private function buildTableName($name, $schema)
11
    {
12
        return ($schema === false || $schema == '' ? '' : "\"{$schema}\".") . "\"$name\"";
13
    }
14
15
    private function setSearchField($details)
16
    {
17
        if ($details['schema'] != null) {
18
            $this->query(
19
                sprintf('SET search_path TO "%s", public', $details['schema'])
20
            );
21
        }
22
    }
23
24
    protected function getDriverName()
25
    {
26
        return 'pgsql';
27
    }
28
29
    #[\Override]
30
    protected function _addSchema($name)
31
    {
32
        $this->query(sprintf('CREATE SCHEMA "%s"', $name));
33
    }
34
35
    #[\Override]
36
    protected function _dropSchema($name)
37
    {
38
        $this->query(sprintf('DROP SCHEMA "%s"', $name));
39
    }
40
41
    #[\Override]
42
    protected function _addTable($details)
43
    {
44
        $this->query(sprintf('CREATE TABLE  %s ()', $this->buildTableName($details['name'], $details['schema'])));
45
    }
46
47
    #[\Override]
48
    protected function _addView($details)
49
    {
50
        $this->setSearchField($details);
51
        $this->query(sprintf('CREATE VIEW %s AS %s', $this->buildTableName($details['name'], $details['schema']), $details['definition']));
52
    }
53
54
    #[\Override]
55
    protected function _dropView($details)
56
    {
57
        $this->query(sprintf('DROP VIEW %s', $this->buildTableName($details['name'], $details['schema'])));
58
    }
59
60
    #[\Override]
61
    protected function _changeViewDefinition($details)
62
    {
63
        $this->setSearchField($details['to']);
64
        $name = $this->buildTableName($details['to']['name'], $details['to']['schema']);
65
        $this->query(sprintf("CREATE OR REPLACE VIEW %s AS %s", $name, $details['to']['definition']));
66
    }
67
68
    #[\Override]
69
    protected function _dropTable($details)
70
    {
71
        $primaryKey = $this->query(
72
            "select column_default from 
73
                information_schema.table_constraints pk 
74
                join information_schema.key_column_usage c on 
75
                   c.table_name = pk.table_name and 
76
                   c.constraint_name = pk.constraint_name and
77
                   c.constraint_schema = pk.table_schema
78
                join information_schema.columns cl on
79
                    cl.table_name = c.table_name and
80
                    cl.table_schema = pk.table_schema and
81
                    cl.column_name = c.column_name
82
83
                where pk.table_name = ? and pk.table_schema=?
84
                and constraint_type = 'PRIMARY KEY'", array($details['name'], $details['schema'] == '' ? $this->getDefaultSchema() : $details['schema'])
85
        );
86
        $this->query(sprintf('DROP TABLE %s', $this->buildTableName($details['name'], $details['schema'])));
87
88
        if ($primaryKey) {
89
            if (preg_match("/nextval\(\'(?<sequence>.*)\'\:\:regclass\)/i", $primaryKey[0]['column_default'] ?? '', $matches)) {
90
                $this->query(sprintf('DROP SEQUENCE IF EXISTS "%s"', $matches['sequence']));
91
            }
92
        }
93
    }
94
95
    public function describe()
96
    {
97
        $descriptor = new \yentu\descriptors\Postgresql($this);
0 ignored issues
show
The type yentu\descriptors\Postgresql 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...
98
        return $descriptor->describe();
99
    }
100
101
    #[\Override]
102
    protected function _addColumn($details)
103
    {
104
        $this->query(
105
            sprintf('ALTER TABLE %s ADD COlUMN "%s" %s', $this->buildTableName($details['table'], $details['schema']), $details['name'], $this->convertTypes(
106
                $details['type'], self::CONVERT_TO_DRIVER, $details['length']
107
            )
108
            )
109
        );
110
        $this->_changeColumnNulls($details);
111
        $this->_changeColumnDefault($details);
112
    }
113
114
    #[\Override]
115
    protected function _dropColumn($details)
116
    {
117
        $this->query(
118
            sprintf(
119
                'ALTER TABLE %s DROP COLUMN "%s"', $this->buildTableName($details['table'], $details['schema']), $details['name']
120
            )
121
        );
122
    }
123
124
    #[\Override]
125
    protected function _changeColumnDefault($details)
126
    {
127
        $details = Parameters::wrap($details, ['default']);
128
        $query = 'ALTER TABLE %s ALTER COLUMN "%s" ';
129
        if ($details['default'] != '') {
130
            $tableName = $this->buildTableName($details['table'], $details['schema']);
131
            $columnName = $details['name'];
132
            $default = "SET DEFAULT {$details['default']}";
133
            $this->query(sprintf($query . $default, $tableName, $columnName));
134
        } else if (isset($details['to'])) {
135
            if ($details['to']['default'] != '') {
136
                $tableName = $this->buildTableName($details['to']['table'], $details['to']['schema']);
137
                $columnName = $details['to']['name'];
138
                $default = "SET DEFAULT {$details['to']['default']}";
139
            } else if ($details['to']['default'] == '' && $details['from']['default'] != '') {
140
                $tableName = $this->buildTableName($details['to']['table'], $details['to']['schema']);
141
                $columnName = $details['to']['name'];
142
                $default = "DROP DEFAULT";
143
            }
144
            $this->query(sprintf($query . $default, $tableName, $columnName));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $tableName does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $default does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $columnName does not seem to be defined for all execution paths leading up to this point.
Loading history...
145
        }
146
    }
147
148
    /**
149
     *
150
     * @param array $details
151
     */
152
    #[\Override]
153
    protected function _changeColumnNulls($details)
154
    {
155
        $details = Parameters::wrap($details, ['nulls']);
156
        $query = 'ALTER TABLE %s ALTER COLUMN "%s" ';
157
158
        if ($details['nulls'] === false) {
159
            $tableName = $this->buildTableName($details['table'], $details['schema']);
160
            $columnName = $details['name'];
161
            $null = ' SET NOT NULL';
162
            $this->query(sprintf($query . $null, $tableName, $columnName));
163
        } else if (isset($details['to'])) {
164
            if ($details['to']['nulls'] === false) {
165
                $tableName = $this->buildTableName($details['to']['table'], $details['to']['schema']);
166
                $columnName = $details['to']['name'];
167
                $null = ' SET NOT NULL';
168
            } else {
169
                $tableName = $this->buildTableName($details['to']['table'], $details['to']['schema']);
170
                $columnName = $details['to']['name'];
171
                $null = ' DROP NOT NULL';
172
            }
173
            $this->query(sprintf($query . $null, $tableName, $columnName));
174
        }
175
    }
176
177
    #[\Override]
178
    protected function _changeColumnName($details)
179
    {
180
        $this->query(
181
            sprintf(
182
                'ALTER TABLE %s RENAME COLUMN "%s" TO "%s"', $this->buildTableName($details['to']['table'], $details['to']['schema']), $details['from']['name'], $details['to']['name']
183
            )
184
        );
185
    }
186
187
    #[\Override]
188
    protected function _addPrimaryKey($details)
189
    {
190
        $this->query(
191
            sprintf(
192
                'ALTER TABLE %s ADD CONSTRAINT "%s" PRIMARY KEY ("%s")', $this->buildTableName($details['table'], $details['schema']), $details['name'], implode('","', $details['columns'])
193
            )
194
        );
195
    }
196
197
    #[\Override]
198
    protected function _dropPrimaryKey($details)
199
    {
200
        $this->query(
201
            sprintf(
202
                'ALTER TABLE %s DROP CONSTRAINT "%s" CASCADE', $this->buildTableName($details['table'], $details['schema']), $details['name']
203
            )
204
        );
205
    }
206
207
    #[\Override]
208
    protected function _addUniqueKey($details)
209
    {
210
        $this->query(
211
            sprintf(
212
                'ALTER TABLE %s ADD CONSTRAINT "%s" UNIQUE ("%s")', $this->buildTableName($details['table'], $details['schema']), $details['name'], implode('","', $details['columns'])
213
            )
214
        );
215
    }
216
217
    private function dropKeyItem($details, $type)
0 ignored issues
show
The parameter $type is not used and could be removed. ( Ignorable by Annotation )

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

217
    private function dropKeyItem($details, /** @scrutinizer ignore-unused */ $type)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
218
    {
219
        $this->query(
220
            sprintf(
221
                'ALTER TABLE %s DROP CONSTRAINT "%s" CASCADE', $this->buildTableName($details['table'], $details['schema']), $details['name']
222
            )
223
        );
224
225
        //$this->dropTableItem($details, $type);
226
    }
227
228
    #[\Override]
229
    protected function _dropUniqueKey($details)
230
    {
231
        $this->dropKeyItem($details, 'unique_keys');
232
    }
233
234
    #[\Override]
235
    protected function _addAutoPrimaryKey($details)
236
    {
237
        $sequence = $this->buildTableName("{$details['table']}_{$details['column']}_seq", $details['schema']);
238
        $this->query("CREATE SEQUENCE $sequence");
239
        $this->query(
240
            sprintf(
241
                'ALTER TABLE %s ALTER COLUMN "%s" SET DEFAULT nextval(\'%s\')', $this->buildTableName($details['table'],
242
                $details['schema']), $details['column'], $sequence
243
            )
244
        );
245
    }
246
247
    #[\Override]
248
    protected function _dropAutoPrimaryKey($details)
249
    {
250
        $sequence = $this->buildTableName("{$details['table']}_{$details['column']}_seq", $details['schema']);
251
        $this->query(
252
            sprintf(
253
                'ALTER TABLE %s ALTER COLUMN "%s" SET DEFAULT NULL', $this->buildTableName($details['table'],
254
                $details['schema']), $details['column'], $sequence
255
            )
256
        );
257
        $this->query("DROP SEQUENCE IF EXISTS $sequence");
258
    }
259
260
    #[\Override]
261
    protected function _addForeignKey($details)
262
    {
263
        $this->query(
264
            sprintf(
265
                'ALTER TABLE %s ADD CONSTRAINT "%s" FOREIGN KEY ("%s") REFERENCES %s ("%s") MATCH FULL ON DELETE %s ON UPDATE %s',
266
                $this->buildTableName($details['table'], $details['schema']), $details['name'],
267
                implode('","', $details['columns']), $this->buildTableName($details['foreign_table'], $details['foreign_schema']),
268
                implode('","', $details['foreign_columns']),
269
                $details['on_delete'] == '' ? 'NO ACTION' : $details['on_delete'], $details['on_update'] == '' ? 'NO ACTION' : $details['on_update']
270
            )
271
        );
272
    }
273
274
    #[\Override]
275
    protected function _dropForeignKey($details)
276
    {
277
        $this->dropKeyItem($details, 'foreign_keys');
278
    }
279
280
    #[\Override]
281
    protected function _addIndex($details)
282
    {
283
        $this->query(
284
            sprintf(
285
                'CREATE INDEX %s "%s" ON %s ("%s")', $details['unique'] ? 'UNIQUE' : '', $details['name'],
286
                $this->buildTableName($details['table'], $details['schema']), implode('", "', $details['columns'])
287
            )
288
        );
289
    }
290
291
    #[\Override]
292
    protected function _dropIndex($details)
293
    {
294
        $this->query(sprintf('DROP INDEX %s', $this->buildTableName($details['name'], $details['schema'])));
295
    }
296
297
    #[\Override]
298
    public function quoteIdentifier(string $identifier): string
299
    {
300
        return "\"$identifier\"";
301
    }
302
303
    #[\Override]
304
    public function convertTypes($type, $direction, $length = null)
305
    {
306
        $types = array(
307
            'integer' => 'integer',
308
            'bigint' => 'bigint',
309
            'character varying' => 'string',
310
            'numeric' => 'double',
311
            'timestamp with time zone' => 'timestamp',
312
            'timestamp without time zone' => 'timestamp',
313
            'text' => 'text',
314
            'boolean' => 'boolean',
315
            'date' => 'date',
316
            'bytea' => 'blob',
317
            'oid' => 'bigint'
318
        );
319
320
        switch ($direction) {
321
            case self::CONVERT_TO_YENTU:
322
                $destinationType = $types[strtolower($type)];
323
                break;
324
325
            case self::CONVERT_TO_DRIVER:
326
                $destinationType = array_search(strtolower($type), $types);
327
                break;
328
        }
329
330
        if ($destinationType == '') {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $destinationType does not seem to be defined for all execution paths leading up to this point.
Loading history...
331
            throw new \yentu\exceptions\DatabaseManipulatorException("Invalid data type {$type} requested");
332
        } else if ($destinationType == 'character varying') {
333
            $destinationType .= $length === null ? '' : "($length)";
334
        }
335
336
        return $destinationType;
337
    }
338
339
    #[\Override]
340
    protected function _changeTableName($details)
341
    {
342
        $this->query(sprintf(
343
                "ALTER TABLE %s RENAME TO %s", $this->buildTableName($details['from']['name'],
344
                $details['from']['schema']), $this->buildTableName($details['to']['name'], false)
345
            )
346
        );
347
    }
348
}
349