Completed
Push — master ( 360c85...21cb38 )
by José
09:01 queued 07:03
created

PostgresAdapter::connect()   C

Complexity

Conditions 8
Paths 10

Size

Total Lines 43
Code Lines 26

Duplication

Lines 5
Ratio 11.63 %

Code Coverage

Tests 20
CRAP Score 8.0069

Importance

Changes 0
Metric Value
dl 5
loc 43
ccs 20
cts 21
cp 0.9524
rs 5.3846
c 0
b 0
f 0
cc 8
eloc 26
nc 10
nop 0
crap 8.0069
1
<?php
2
/**
3
 * Phinx
4
 *
5
 * (The MIT license)
6
 * Copyright (c) 2015 Rob Morgan
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
9
 * of this software and associated * documentation files (the "Software"), to
10
 * deal in the Software without restriction, including without limitation the
11
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12
 * sell copies of the Software, and to permit persons to whom the Software is
13
 * furnished to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in
16
 * all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24
 * IN THE SOFTWARE.
25
 *
26
 * @package    Phinx
27
 * @subpackage Phinx\Db\Adapter
28
 */
29
namespace Phinx\Db\Adapter;
30
31
use Phinx\Db\Table;
32
use Phinx\Db\Table\Column;
33
use Phinx\Db\Table\ForeignKey;
34
use Phinx\Db\Table\Index;
35
use Phinx\Util\Literal;
36
37
class PostgresAdapter extends PdoAdapter implements AdapterInterface
38
{
39
    const INT_SMALL = 65535;
40
41
    /**
42
     * Columns with comments
43
     *
44
     * @var array
45
     */
46
    protected $columnsWithComments = [];
47
48
    /**
49
     * {@inheritdoc}
50 68
     */
51
    public function connect()
52 68
    {
53 68
        if ($this->connection === null) {
54
            if (!class_exists('PDO') || !in_array('pgsql', \PDO::getAvailableDrivers(), true)) {
55
                // @codeCoverageIgnoreStart
56
                throw new \RuntimeException('You need to enable the PDO_Pgsql extension for Phinx to run properly.');
57
                // @codeCoverageIgnoreEnd
58
            }
59 68
60 68
            $db = null;
61
            $options = $this->getOptions();
62
63 68
            // if port is specified use it, otherwise use the PostgreSQL default
64 68 View Code Duplication
            if (isset($options['port'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
65 68
                $dsn = 'pgsql:host=' . $options['host'] . ';port=' . $options['port'] . ';dbname=' . $options['name'];
66 1
            } else {
67
                $dsn = 'pgsql:host=' . $options['host'] . ';dbname=' . $options['name'];
68
            }
69
70 68
            try {
71 68
                $db = new \PDO($dsn, $options['user'], $options['pass'], [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]);
72 1
            } catch (\PDOException $exception) {
73 1
                throw new \InvalidArgumentException(sprintf(
74 1
                    'There was a problem connecting to the database: %s',
75 1
                    $exception->getMessage()
76
                ), $exception->getCode(), $exception);
77
            }
78 68
79 68
            try {
80 68
                if (isset($options['schema'])) {
81
                    $db->exec('SET search_path TO ' . $options['schema']);
82
                }
83
            } catch (\PDOException $exception) {
84
                throw new \InvalidArgumentException(
85 68
                    sprintf('Schema does not exists: %s', $options['schema']),
86
                    $exception->getCode(),
87 68
                    $exception
88 68
                );
89
            }
90
91
            $this->setConnection($db);
92
        }
93
    }
94
95
    /**
96
     * {@inheritdoc}
97
     */
98
    public function disconnect()
99
    {
100
        $this->connection = null;
101
    }
102
103
    /**
104
     * {@inheritdoc}
105
     */
106
    public function hasTransactions()
107
    {
108
        return true;
109
    }
110
111
    /**
112
     * {@inheritdoc}
113
     */
114
    public function beginTransaction()
115
    {
116
        $this->execute('BEGIN');
117
    }
118
119
    /**
120
     * {@inheritdoc}
121
     */
122
    public function commitTransaction()
123
    {
124
        $this->execute('COMMIT');
125
    }
126
127
    /**
128 68
     * {@inheritdoc}
129
     */
130 68
    public function rollbackTransaction()
131
    {
132
        $this->execute('ROLLBACK');
133
    }
134
135
    /**
136 68
     * Quotes a schema name for use in a query.
137
     *
138 68
     * @param string $schemaName Schema Name
139
     * @return string
140
     */
141
    public function quoteSchemaName($schemaName)
142
    {
143
        return $this->quoteColumnName($schemaName);
144 68
    }
145
146 68
    /**
147
     * {@inheritdoc}
148
     */
149
    public function quoteTableName($tableName)
150
    {
151
        return $this->quoteSchemaName($this->getSchemaName()) . '.' . $this->quoteColumnName($tableName);
152 68
    }
153
154 68
    /**
155 68
     * {@inheritdoc}
156
     */
157
    public function quoteColumnName($columnName)
158
    {
159 68
        return '"' . $columnName . '"';
160 68
    }
161 68
162 68
    /**
163 68
     * {@inheritdoc}
164
     */
165 68
    public function hasTable($tableName)
166
    {
167
        $result = $this->getConnection()->query(
168
            sprintf(
169
                'SELECT *
170
                FROM information_schema.tables
171 68
                WHERE table_schema = %s
172
                AND lower(table_name) = lower(%s)',
173 68
                $this->getConnection()->quote($this->getSchemaName()),
174
                $this->getConnection()->quote($tableName)
175
            )
176 68
        );
177 68
178 48
        return $result->rowCount() === 1;
179 48
    }
180 48
181 48
    /**
182
     * {@inheritdoc}
183 48
     */
184 48
    public function createTable(Table $table)
185 68
    {
186
        $options = $table->getOptions();
187 2
188 2
         // Add the default primary key
189 2
        $columns = $table->getPendingColumns();
190 2 View Code Duplication
        if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
191
            $column = new Column();
192 2
            $column->setName('id')
193 2
                   ->setType('integer')
194 2
                   ->setIdentity(true);
195
196
            array_unshift($columns, $column);
197 68
            $options['primary_key'] = 'id';
198 68
        } elseif (isset($options['id']) && is_string($options['id'])) {
199
            // Handle id => "field_name" to support AUTO_INCREMENT
200 68
            $column = new Column();
201 68
            $column->setName($options['id'])
202 68
                   ->setType('integer')
203
                   ->setIdentity(true);
204
205 68
            array_unshift($columns, $column);
206 6
            $options['primary_key'] = $options['id'];
207 6
        }
208 68
209
        // TODO - process table options like collation etc
210
        $sql = 'CREATE TABLE ';
211 68
        $sql .= $this->quoteTableName($table->getName()) . ' (';
212 68
213 68
        $this->columnsWithComments = [];
214 68 View Code Duplication
        foreach ($columns as $column) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
215 68
            $sql .= $this->quoteColumnName($column->getName()) . ' ' . $this->getColumnSqlDefinition($column) . ', ';
216 68
217
            // set column comments, if needed
218
            if ($column->getComment()) {
219 1
                $this->columnsWithComments[] = $column;
220 1
            }
221 1
        }
222 1
223 1
         // set the primary key(s)
224 1 View Code Duplication
        if (isset($options['primary_key'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
225 1
            $sql = rtrim($sql);
226 1
            $sql .= sprintf(' CONSTRAINT %s_pkey PRIMARY KEY (', $table->getName());
227 1
            if (is_string($options['primary_key'])) { // handle primary_key => 'id'
228 1
                $sql .= $this->quoteColumnName($options['primary_key']);
229 68
            } elseif (is_array($options['primary_key'])) { // handle primary_key => array('tag_id', 'resource_id')
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
230 68
                $sql .= implode(',', array_map([$this, 'quoteColumnName'], $options['primary_key']));
231 2
            }
232
            $sql .= ')';
233
        } else {
234
            $sql = rtrim($sql, ', '); // no primary keys
235 68
        }
236 68
237 1
        // set the foreign keys
238 1
        $foreignKeys = $table->getForeignKeys();
239 1
        if (!empty($foreignKeys)) {
240 1
            foreach ($foreignKeys as $foreignKey) {
241
                $sql .= ', ' . $this->getForeignKeySqlDefinition($foreignKey, $table->getName());
242 68
            }
243
        }
244
245 68
        $sql .= ');';
246 6
247 6
        // process column comments
248 6
        if (!empty($this->columnsWithComments)) {
249 6
            foreach ($this->columnsWithComments as $column) {
250
                $sql .= $this->getColumnCommentSqlDefinition($column, $table->getName());
251
            }
252
        }
253 68
254 68
        // set the indexes
255 5
        $indexes = $table->getIndexes();
256 5
        if (!empty($indexes)) {
257 5
            foreach ($indexes as $index) {
258 5
                $sql .= $this->getIndexSqlDefinition($index, $table->getName());
259
            }
260
        }
261 68
262
        // execute the sql
263
        $this->execute($sql);
264 68
265 1
        // process table comments
266 1
        if (isset($options['comment'])) {
267 1
            $sql = sprintf(
268 1
                'COMMENT ON TABLE %s IS %s',
269 1
                $this->quoteTableName($table->getName()),
270 1
                $this->getConnection()->quote($options['comment'])
271 1
            );
272 68
            $this->execute($sql);
273
        }
274
    }
275
276
    /**
277 1
     * {@inheritdoc}
278
     */
279 1
    public function renameTable($tableName, $newTableName)
280 1
    {
281 1
        $sql = sprintf(
282 1
            'ALTER TABLE %s RENAME TO %s',
283 1
            $this->quoteTableName($tableName),
284 1
            $this->quoteColumnName($newTableName)
285 1
        );
286
        $this->execute($sql);
287
    }
288
289
    /**
290 1
     * {@inheritdoc}
291
     */
292 1
    public function dropTable($tableName)
293 1
    {
294
        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tableName)));
295
    }
296
297
    /**
298 1
     * {@inheritdoc}
299
     */
300 1
    public function truncateTable($tableName)
301 1
    {
302 1
        $sql = sprintf(
303 1
            'TRUNCATE TABLE %s',
304
            $this->quoteTableName($tableName)
305 1
        );
306 1
307
        $this->execute($sql);
308
    }
309
310
    /**
311 9
     * {@inheritdoc}
312
     */
313 9
    public function getColumns($tableName)
314 9
    {
315
        $columns = [];
316
        $sql = sprintf(
317
            "SELECT column_name, data_type, udt_name, is_identity, is_nullable,
318 9
             column_default, character_maximum_length, numeric_precision, numeric_scale
319
             FROM information_schema.columns
320 9
             WHERE table_name ='%s'",
321 9
            $tableName
322
        );
323 9
        $columnsInfo = $this->fetchAll($sql);
324 9
325 9
        foreach ($columnsInfo as $columnInfo) {
326 9
            $isUserDefined = $columnInfo['data_type'] === 'USER-DEFINED';
327 9
            if ($isUserDefined) {
328 9
                $columnType = Literal::from($columnInfo['udt_name']);
329 9
            } else {
330 9
                $columnType = $this->getPhinxType($columnInfo['data_type']);
331 9
            }
332
            // If the default value begins with a ' or looks like a function mark it as literal
333 9
            if (isset($columnInfo['column_default'][0]) && $columnInfo['column_default'][0] === "'") {
334 1
                if (preg_match('/^\'(.*)\'::[^:]+$/', $columnInfo['column_default'], $match)) {
335 1
                    // '' and \' are replaced with a single '
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
336
                    $columnDefault = preg_replace('/[\'\\\\]\'/', "'", $match[1]);
337 9
                } else {
338 5
                    $columnDefault = Literal::from($columnInfo['column_default']);
339 5
                }
340 9
            } elseif (preg_match('/^\D[a-z_\d]*\(.*\)$/', $columnInfo['column_default'])) {
341 9
                $columnDefault = Literal::from($columnInfo['column_default']);
342 9
            } else {
343
                $columnDefault = $columnInfo['column_default'];
344
            }
345
346
            $column = new Column();
347
            $column->setName($columnInfo['column_name'])
348 24
                   ->setType($columnType)
349
                   ->setNull($columnInfo['is_nullable'] === 'YES')
350 24
                   ->setDefault($columnDefault)
351
                   ->setIdentity($columnInfo['is_identity'] === 'YES')
352
                   ->setPrecision($columnInfo['numeric_precision'])
353 24
                   ->setScale($columnInfo['numeric_scale']);
354 24
355 24
            if (preg_match('/\bwith time zone$/', $columnInfo['data_type'])) {
356
                $column->setTimezone(true);
357 24
            }
358
359 24
            if (isset($columnInfo['character_maximum_length'])) {
360 24
                $column->setLimit($columnInfo['character_maximum_length']);
361
            }
362
            $columns[] = $column;
363
        }
364
365
        return $columns;
366 18
    }
367
368 18
    /**
369 18
     * {@inheritdoc}
370 18
     */
371 18 View Code Duplication
    public function hasColumn($tableName, $columnName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
372 18
    {
373 18
        $sql = sprintf(
374
            "SELECT count(*)
375 18
            FROM information_schema.columns
376 18
            WHERE table_schema = '%s' AND table_name = '%s' AND column_name = '%s'",
377
            $this->getSchemaName(),
378
            $tableName,
379
            $columnName
380
        );
381 3
382
        $result = $this->fetchRow($sql);
383 3
384
        return $result['count'] > 0;
385
    }
386 3
387 3
    /**
388
     * {@inheritdoc}
389 3
     */
390 3 View Code Duplication
    public function addColumn(Table $table, Column $column)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
391 3
    {
392 1
        $sql = sprintf(
393
            'ALTER TABLE %s ADD %s %s;',
394 2
            $this->quoteTableName($table->getName()),
395 2
            $this->quoteColumnName($column->getName()),
396 2
            $this->getColumnSqlDefinition($column)
397 2
        );
398 2
399 2
        if ($column->getComment()) {
400 2
            $sql .= $this->getColumnCommentSqlDefinition($column, $table->getName());
401 2
        }
402 2
403
        $this->execute($sql);
404
    }
405
406
    /**
407 5
     * {@inheritdoc}
408
     */
409
    public function renameColumn($tableName, $columnName, $newColumnName)
410
    {
411 5
        $sql = sprintf(
412 5
            "SELECT CASE WHEN COUNT(*) > 0 THEN 1 ELSE 0 END AS column_exists
413 5
             FROM information_schema.columns
414 5
             WHERE table_name ='%s' AND column_name = '%s'",
415 5
            $tableName,
416 5
            $columnName
417
        );
418 5
        $result = $this->fetchRow($sql);
419 5
        if (!(bool)$result['column_exists']) {
420
            throw new \InvalidArgumentException("The specified column does not exist: $columnName");
421 5
        }
422 5
        $this->execute(
423
            sprintf(
424 5
                'ALTER TABLE %s RENAME COLUMN %s TO %s',
425 5
                $this->quoteTableName($tableName),
426 5
                $this->quoteColumnName($columnName),
427 5
                $this->quoteColumnName($newColumnName)
428 5
            )
429 5
        );
430 2
    }
431 2
432 4
    /**
433
     * {@inheritdoc}
434 5
     */
435 5
    public function changeColumn($tableName, $columnName, Column $newColumn)
436
    {
437 1
        // TODO - is it possible to merge these 3 queries into less?
438 1
        // change data type
439 1
        $sql = sprintf(
440 1
            'ALTER TABLE %s ALTER COLUMN %s TYPE %s',
441 1
            $this->quoteTableName($tableName),
442 1
            $this->quoteColumnName($columnName),
443 1
            $this->getColumnSqlDefinition($newColumn)
444 1
        );
445 1
        //NULL and DEFAULT cannot be set while changing column type
446
        $sql = preg_replace('/ NOT NULL/', '', $sql);
447 4
        $sql = preg_replace('/ NULL/', '', $sql);
448 4
        //If it is set, DEFAULT is the last definition
449 4
        $sql = preg_replace('/DEFAULT .*/', '', $sql);
450 4
        $this->execute($sql);
451 4
        // process null
452 4
        $sql = sprintf(
453 4
            'ALTER TABLE %s ALTER COLUMN %s',
454
            $this->quoteTableName($tableName),
455
            $this->quoteColumnName($columnName)
456 5
        );
457 1
        if ($newColumn->isNull()) {
458 1
            $sql .= ' DROP NOT NULL';
459 1
        } else {
460 1
            $sql .= ' SET NOT NULL';
461 1
        }
462 1
        $this->execute($sql);
463 1
        if (!is_null($newColumn->getDefault())) {
464 1
            //change default
465 1
            $this->execute(
466
                sprintf(
467
                    'ALTER TABLE %s ALTER COLUMN %s SET %s',
468 5
                    $this->quoteTableName($tableName),
469 2
                    $this->quoteColumnName($columnName),
470 2
                    $this->getDefaultValueDefinition($newColumn->getDefault())
471 2
                )
472 5
            );
473
        } else {
474
            //drop default
475
            $this->execute(
476
                sprintf(
477 1
                    'ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT',
478
                    $this->quoteTableName($tableName),
479 1
                    $this->quoteColumnName($columnName)
480 1
                )
481 1
            );
482 1
        }
483 1
        // rename column
484 1
        if ($columnName !== $newColumn->getName()) {
485 1
            $this->execute(
486 1
                sprintf(
487
                    'ALTER TABLE %s RENAME COLUMN %s TO %s',
488
                    $this->quoteTableName($tableName),
489
                    $this->quoteColumnName($columnName),
490
                    $this->quoteColumnName($newColumn->getName())
491
                )
492
            );
493
        }
494 9
495
        // change column comment if needed
496 9
        if ($newColumn->getComment()) {
497
            $sql = $this->getColumnCommentSqlDefinition($newColumn, $tableName);
498
            $this->execute($sql);
499
        }
500
    }
501
502
    /**
503
     * {@inheritdoc}
504
     */
505
    public function dropColumn($tableName, $columnName)
506
    {
507
        $this->execute(
508
            sprintf(
509
                'ALTER TABLE %s DROP COLUMN %s',
510
                $this->quoteTableName($tableName),
511
                $this->quoteColumnName($columnName)
512
            )
513
        );
514 9
    }
515 9
516 9
    /**
517 9
     * Get an array of indexes from a particular table.
518 9
     *
519 9
     * @param string $tableName Table Name
520 9
     * @return array
521 9
     */
522 9 View Code Duplication
    protected function getIndexes($tableName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
523
    {
524
        $indexes = [];
525
        $sql = "SELECT
526
            i.relname AS index_name,
527
            a.attname AS column_name
528 9
        FROM
529
            pg_class t,
530 9
            pg_class i,
531 4
            pg_index ix,
532 4
            pg_attribute a
533 9
        WHERE
534 9
            t.oid = ix.indrelid
535 9
            AND i.oid = ix.indexrelid
536 9
            AND a.attrelid = t.oid
537 9
            AND a.attnum = ANY(ix.indkey)
538
            AND t.relkind = 'r'
539 8
            AND t.relname = '$tableName'
540 8
        ORDER BY
541
            t.relname,
542
            i.relname;";
543
        $rows = $this->fetchAll($sql);
544
        foreach ($rows as $row) {
545
            if (!isset($indexes[$row['index_name']])) {
546 1
                $indexes[$row['index_name']] = ['columns' => []];
547
            }
548 1
            $indexes[$row['index_name']]['columns'][] = strtolower($row['column_name']);
549 1
        }
550 1
551 1
        return $indexes;
552
    }
553
554
    /**
555
     * {@inheritdoc}
556
     */
557 View Code Duplication
    public function hasIndex($tableName, $columns)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
558
    {
559
        if (is_string($columns)) {
560 2
            $columns = [$columns];
561
        }
562 2
        $columns = array_map('strtolower', $columns);
563 2
        $indexes = $this->getIndexes($tableName);
564 2
        foreach ($indexes as $index) {
565
            if (array_diff($index['columns'], $columns) === array_diff($columns, $index['columns'])) {
566
                return true;
567
            }
568
        }
569 1
570
        return false;
571 1
    }
572 1
573 1
    /**
574
     * {@inheritdoc}
575 1
     */
576 1 View Code Duplication
    public function hasIndexByName($tableName, $indexName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
577
    {
578 1
        $indexes = $this->getIndexes($tableName);
579 1
        foreach ($indexes as $name => $index) {
580 1
            if ($name === $indexName) {
581 1
                return true;
582 1
            }
583 1
        }
584 1
585 1
        return false;
586 1
    }
587
588 1
    /**
589
     * {@inheritdoc}
590
     */
591
    public function addIndex(Table $table, Index $index)
592
    {
593
        $sql = $this->getIndexSqlDefinition($index, $table->getName());
594
        $this->execute($sql);
595
    }
596 1
597
    /**
598 1
     * {@inheritdoc}
599 1
     */
600
    public function dropIndex($tableName, $columns)
601 1
    {
602 1
        if (is_string($columns)) {
603 1
            $columns = [$columns]; // str to array
604
        }
605
606
        $indexes = $this->getIndexes($tableName);
607
        $columns = array_map('strtolower', $columns);
608 3
609
        foreach ($indexes as $indexName => $index) {
610 3
            $a = array_diff($columns, $index['columns']);
611 1
            if (empty($a)) {
612 1
                $this->execute(
613 3
                    sprintf(
614 3
                        'DROP INDEX IF EXISTS %s',
615
                        $this->quoteColumnName($indexName)
616
                    )
617
                );
618
619
                return;
620 3
            }
621 3
        }
622 3
    }
623 3
624
    /**
625 1
     * {@inheritdoc}
626 1
     */
627
    public function dropIndexByName($tableName, $indexName)
628
    {
629
        $sql = sprintf(
630
            'DROP INDEX IF EXISTS %s',
631
            $indexName
632
        );
633
        $this->execute($sql);
634
    }
635
636 3
    /**
637
     * {@inheritdoc}
638 3
     */
639 3 View Code Duplication
    public function hasForeignKey($tableName, $columns, $constraint = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
640
    {
641
        if (is_string($columns)) {
642
            $columns = [$columns]; // str to array
643
        }
644
        $foreignKeys = $this->getForeignKeys($tableName);
645
        if ($constraint) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $constraint of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
646
            if (isset($foreignKeys[$constraint])) {
647
                return !empty($foreignKeys[$constraint]);
648
            }
649
650 3
            return false;
651
        } else {
652 3
            foreach ($foreignKeys as $key) {
653 3
                $a = array_diff($columns, $key['columns']);
654 3
                if (empty($a)) {
655 3
                    return true;
656 3
                }
657 3
            }
658 3
659 3
            return false;
660
        }
661
    }
662
663
    /**
664
     * Get an array of foreign keys from a particular table.
665 2
     *
666
     * @param string $tableName Table Name
667 2
     * @return array
668 2
     */
669 2 View Code Duplication
    protected function getForeignKeys($tableName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
670 2
    {
671 2
        $foreignKeys = [];
672 2
        $rows = $this->fetchAll(sprintf(
673 2
            "SELECT
674
                    tc.constraint_name,
675
                    tc.table_name, kcu.column_name,
676
                    ccu.table_name AS referenced_table_name,
677
                    ccu.column_name AS referenced_column_name
678 1
                FROM
679
                    information_schema.table_constraints AS tc
680 1
                    JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
681
                    JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
682
                WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name = '%s'
683
                ORDER BY kcu.position_in_unique_constraint",
684 1
            $tableName
685 1
        ));
686 1
        foreach ($rows as $row) {
687 1
            $foreignKeys[$row['constraint_name']]['table'] = $row['table_name'];
688 1
            $foreignKeys[$row['constraint_name']]['columns'][] = $row['column_name'];
689
            $foreignKeys[$row['constraint_name']]['referenced_table'] = $row['referenced_table_name'];
690 1
            $foreignKeys[$row['constraint_name']]['referenced_columns'][] = $row['referenced_column_name'];
691 1
        }
692 1
693 1
        return $foreignKeys;
694 1
    }
695
696
    /**
697
     * {@inheritdoc}
698
     */
699 View Code Duplication
    public function addForeignKey(Table $table, ForeignKey $foreignKey)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
700
    {
701 1
        $sql = sprintf(
702 1
            'ALTER TABLE %s ADD %s',
703
            $this->quoteTableName($table->getName()),
704 1
            $this->getForeignKeySqlDefinition($foreignKey, $table->getName())
705
        );
706 1
        $this->execute($sql);
707 1
    }
708 1
709 1
    /**
710
     * {@inheritdoc}
711 1
     */
712 View Code Duplication
    public function dropForeignKey($tableName, $columns, $constraint = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
713
    {
714
        if (is_string($columns)) {
715
            $columns = [$columns]; // str to array
716 68
        }
717
718
        if ($constraint) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $constraint of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
719 68
            $this->execute(
720 14
                sprintf(
721
                    'ALTER TABLE %s DROP CONSTRAINT %s',
722 1
                    $this->quoteTableName($tableName),
723
                    $constraint
724 1
                )
725
            );
726 14
        } else {
727 68
            foreach ($columns as $column) {
728 68
                $rows = $this->fetchAll(sprintf(
729 68
                    "SELECT CONSTRAINT_NAME
730 68
                      FROM information_schema.KEY_COLUMN_USAGE
731 68
                      WHERE TABLE_SCHEMA = CURRENT_SCHEMA()
732 68
                        AND TABLE_NAME IS NOT NULL
733 68
                        AND TABLE_NAME = '%s'
734 68
                        AND COLUMN_NAME = '%s'
735 68
                      ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
736 68
                    $tableName,
737 68
                    $column
738 68
                ));
739 2
740 68
                foreach ($rows as $row) {
741 68
                    $this->dropForeignKey($tableName, $columns, $row['constraint_name']);
742 68
                }
743
            }
744 68
        }
745 68
    }
746 68
747 1
    /**
748 68
     * {@inheritdoc}
749 68
     */
750 68
    public function getSqlType($type, $limit = null)
751 15
    {
752 15
        switch ($type) {
753 1
            case static::PHINX_TYPE_TEXT:
754
            case static::PHINX_TYPE_TIME:
755
            case static::PHINX_TYPE_DATE:
756
            case static::PHINX_TYPE_BOOLEAN:
757
            case static::PHINX_TYPE_JSON:
758 14
            case static::PHINX_TYPE_JSONB:
759
            case static::PHINX_TYPE_UUID:
760
            case static::PHINX_TYPE_CIDR:
761 14
            case static::PHINX_TYPE_INET:
762
            case static::PHINX_TYPE_MACADDR:
763
            case static::PHINX_TYPE_TIMESTAMP:
764 14
                return ['name' => $type];
765
            case static::PHINX_TYPE_INTEGER:
766
                if ($limit && $limit == static::INT_SMALL) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $limit of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
767 14
                    return [
768
                        'name' => 'smallint',
769
                        'limit' => static::INT_SMALL
770 14
                    ];
771 14
                }
772 13
773
                return ['name' => $type];
774
            case static::PHINX_TYPE_DECIMAL:
775 1
                return ['name' => $type, 'precision' => 18, 'scale' => 0];
776 14
            case static::PHINX_TYPE_STRING:
777
                return ['name' => 'character varying', 'limit' => 255];
778
            case static::PHINX_TYPE_CHAR:
779
                return ['name' => 'character', 'limit' => 255];
780
            case static::PHINX_TYPE_BIG_INTEGER:
781
                return ['name' => 'bigint'];
782
            case static::PHINX_TYPE_FLOAT:
783
                return ['name' => 'real'];
784
            case static::PHINX_TYPE_DATETIME:
785 10
                return ['name' => 'timestamp'];
786
            case static::PHINX_TYPE_BLOB:
787
            case static::PHINX_TYPE_BINARY:
788 10
                return ['name' => 'bytea'];
789 10
            case static::PHINX_TYPE_INTERVAL:
790 6
                return ['name' => 'interval'];
791 10
            // Geospatial database types
792 10
            // Spatial storage in Postgres is done via the PostGIS extension,
793
            // which enables the use of the "geography" type in combination
794 10
            // with SRID 4326.
795 2
            case static::PHINX_TYPE_GEOMETRY:
796 10
                return ['name' => 'geography', 'type' => 'geometry', 'srid' => 4326];
797
            case static::PHINX_TYPE_POINT:
798 10
                return ['name' => 'geography', 'type' => 'point', 'srid' => 4326];
799
            case static::PHINX_TYPE_LINESTRING:
800 10
                return ['name' => 'geography', 'type' => 'linestring', 'srid' => 4326];
801
            case static::PHINX_TYPE_POLYGON:
802 1
                return ['name' => 'geography', 'type' => 'polygon', 'srid' => 4326];
803
            default:
804 1
                if ($this->isArrayType($type)) {
805 10
                    return ['name' => $type];
806 10
                }
807 10
                // Return array type
808 9
                throw new \RuntimeException('The type: "' . $type . '" is not supported');
809 5
        }
810 5
    }
811 3
812 4
    /**
813 4
     * Returns Phinx type by SQL type
814 2
     *
815 4
     * @param string $sqlType SQL type
816 4
     * @returns string Phinx type
817 2
     */
818 4
    public function getPhinxType($sqlType)
819 1
    {
820
        switch ($sqlType) {
821 4
            case 'character varying':
822 4
            case 'varchar':
823 4
                return static::PHINX_TYPE_STRING;
824 4
            case 'character':
825 3
            case 'char':
826 4
                return static::PHINX_TYPE_CHAR;
827 2
            case 'text':
828 4
                return static::PHINX_TYPE_TEXT;
829 4
            case 'json':
830 4
                return static::PHINX_TYPE_JSON;
831 4
            case 'jsonb':
832 3
                return static::PHINX_TYPE_JSONB;
833 3
            case 'smallint':
834 3
                return [
835 3
                    'name' => 'smallint',
836 1
                    'limit' => static::INT_SMALL
837 1
                ];
838
            case 'int':
839
            case 'int4':
840
            case 'integer':
841
                return static::PHINX_TYPE_INTEGER;
842
            case 'decimal':
843
            case 'numeric':
844
                return static::PHINX_TYPE_DECIMAL;
845
            case 'bigint':
846
            case 'int8':
847
                return static::PHINX_TYPE_BIG_INTEGER;
848
            case 'real':
849
            case 'float4':
850
                return static::PHINX_TYPE_FLOAT;
851
            case 'bytea':
852 1
                return static::PHINX_TYPE_BINARY;
853
            case 'interval':
854 1
                return static::PHINX_TYPE_INTERVAL;
855 1
            case 'time':
856 1
            case 'timetz':
857
            case 'time with time zone':
858
            case 'time without time zone':
859
                return static::PHINX_TYPE_TIME;
860
            case 'date':
861 2
                return static::PHINX_TYPE_DATE;
862
            case 'timestamp':
863 2
            case 'timestamptz':
864 2
            case 'timestamp with time zone':
865 2
            case 'timestamp without time zone':
866
                return static::PHINX_TYPE_DATETIME;
867
            case 'bool':
868
            case 'boolean':
869
                return static::PHINX_TYPE_BOOLEAN;
870
            case 'uuid':
871 1
                return static::PHINX_TYPE_UUID;
872
            case 'cidr':
873 1
                return static::PHINX_TYPE_CIDR;
874 1
            case 'inet':
875 1
                return static::PHINX_TYPE_INET;
876 1
            case 'macaddr':
877
                return static::PHINX_TYPE_MACADDR;
878
            default:
879
                throw new \RuntimeException('The PostgreSQL type: "' . $sqlType . '" is not supported');
880
        }
881
    }
882
883
    /**
884 68
     * {@inheritdoc}
885
     */
886 68
    public function createDatabase($name, $options = [])
887 4
    {
888 68
        $charset = isset($options['charset']) ? $options['charset'] : 'utf8';
889 68
        $this->execute(sprintf("CREATE DATABASE %s WITH ENCODING = '%s'", $name, $charset));
890 68
    }
891 68
892
    /**
893
     * {@inheritdoc}
894
     */
895
    public function hasDatabase($databaseName)
896
    {
897
        $sql = sprintf("SELECT count(*) FROM pg_database WHERE datname = '%s'", $databaseName);
898
        $result = $this->fetchRow($sql);
899
900 68
        return $result['count'] > 0;
901
    }
902 68
903 68
    /**
904 50
     * {@inheritdoc}
905 50
     */
906 68
    public function dropDatabase($name)
907 68
    {
908
        $this->disconnect();
909 68
        $this->execute(sprintf('DROP DATABASE IF EXISTS %s', $name));
910 1
        $this->connect();
911 1
    }
912 1
913 1
    /**
914 1
     * Gets the PostgreSQL Column Definition for a Column object.
915 68
     *
916
     * @param \Phinx\Db\Table\Column $column Column
917
     * @return string
918
     */
919
    protected function getColumnSqlDefinition(Column $column)
920
    {
921
        $buffer = [];
922 68
        if ($column->isIdentity()) {
923 68
            $buffer[] = $column->getType() == 'biginteger' ? 'BIGSERIAL' : 'SERIAL';
924 68
        } elseif ($column->getType() instanceof Literal) {
925 68
            $buffer[] = (string)$column->getType();
926 68
        } else {
927
            $sqlType = $this->getSqlType($column->getType(), $column->getLimit());
928
            $buffer[] = strtoupper($sqlType['name']);
929 68
930 68
            // integers cant have limits in postgres
931 68
            if (static::PHINX_TYPE_DECIMAL === $sqlType['name'] && ($column->getPrecision() || $column->getScale())) {
932 68
                $buffer[] = sprintf(
933 1
                    '(%s, %s)',
934 1
                    $column->getPrecision() ?: $sqlType['precision'],
935
                    $column->getScale() ?: $sqlType['scale']
936
                );
937 68
            } elseif (in_array($sqlType['name'], ['geography'])) {
938
                // geography type must be written with geometry type and srid, like this: geography(POLYGON,4326)
939 68
                $buffer[] = sprintf(
940 68
                    '(%s,%s)',
941 68
                    strtoupper($sqlType['type']),
942
                    $sqlType['srid']
943 68
                );
944
            } elseif (!in_array($sqlType['name'], ['integer', 'smallint', 'bigint'])) {
945
                if ($column->getLimit() || isset($sqlType['limit'])) {
946
                    $buffer[] = sprintf('(%s)', $column->getLimit() ?: $sqlType['limit']);
947
                }
948
            }
949
950
            $timeTypes = [
951
                'time',
952
                'timestamp',
953 6
            ];
954
            if (in_array($sqlType['name'], $timeTypes) && $column->isTimezone()) {
955
                $buffer[] = strtoupper('with time zone');
956 6
            }
957 6
        }
958 6
959
        $buffer[] = $column->isNull() ? 'NULL' : 'NOT NULL';
960 6
        $buffer = implode(' ', $buffer);
961 6
962 6
        if (!is_null($column->getDefault())) {
963 6
            $buffer .= $this->getDefaultValueDefinition($column->getDefault());
964
        }
965 6
966
        return $buffer;
967
    }
968
969
    /**
970
     * Gets the PostgreSQL Column Comment Defininition for a column object.
971
     *
972
     * @param \Phinx\Db\Table\Column $column Column
973
     * @param string $tableName Table name
974
     * @return string
975 7
     */
976
    protected function getColumnCommentSqlDefinition(Column $column, $tableName)
977 7
    {
978 3
        // passing 'null' is to remove column comment
979 3
        $comment = (strcasecmp($column->getComment(), 'NULL') !== 0)
980 5
                 ? $this->getConnection()->quote($column->getComment())
981 5
                 : 'NULL';
982
983
        return sprintf(
984 5
            'COMMENT ON COLUMN %s.%s IS %s;',
985
            $this->quoteSchemaName($tableName),
986 7
            $this->quoteColumnName($column->getName()),
987 7
            $comment
988 7
        );
989 7
    }
990 7
991 7
    /**
992 7
     * Gets the PostgreSQL Index Definition for an Index object.
993 7
     *
994
     * @param \Phinx\Db\Table\Index  $index Index
995
     * @param string $tableName Table name
996
     * @return string
997
     */
998
    protected function getIndexSqlDefinition(Index $index, $tableName)
999
    {
1000 View Code Duplication
        if (is_string($index->getName())) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1001
            $indexName = $index->getName();
1002
        } else {
1003 3
            $columnNames = $index->getColumns();
1004
            if (is_string($columnNames)) {
1005 3
                $columnNames = [$columnNames];
1006 3
            }
1007 3
            $indexName = sprintf('%s_%s', $tableName, implode('_', $columnNames));
1008 3
        }
1009
        $def = sprintf(
1010
            "CREATE %s INDEX %s ON %s (%s);",
1011 3
            ($index->getType() === Index::UNIQUE ? 'UNIQUE' : ''),
1012
            $indexName,
1013
            $this->quoteTableName($tableName),
1014 3
            implode(',', array_map([$this, 'quoteColumnName'], $index->getColumns()))
1015
        );
1016
1017
        return $def;
1018
    }
1019
1020 68
    /**
1021
     * Gets the MySQL Foreign Key Definition for an ForeignKey object.
1022
     *
1023 68
     * @param \Phinx\Db\Table\ForeignKey $foreignKey
1024 67
     * @param string     $tableName  Table name
1025 67
     * @return string
1026
     */
1027 68 View Code Duplication
    protected function getForeignKeySqlDefinition(ForeignKey $foreignKey, $tableName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1028
    {
1029 68
        $constraintName = $foreignKey->getConstraint() ?: $tableName . '_' . implode('_', $foreignKey->getColumns());
1030 68
1031
        $def = ' CONSTRAINT "' . $constraintName . '" FOREIGN KEY ("' . implode('", "', $foreignKey->getColumns()) . '")';
1032
        $def .= " REFERENCES {$this->quoteTableName($foreignKey->getReferencedTable()->getName())} (\"" . implode('", "', $foreignKey->getReferencedColumns()) . '")';
1033
        if ($foreignKey->getOnDelete()) {
1034
            $def .= " ON DELETE {$foreignKey->getOnDelete()}";
1035
        }
1036
        if ($foreignKey->getOnUpdate()) {
1037
            $def .= " ON UPDATE {$foreignKey->getOnUpdate()}";
1038 68
        }
1039
1040 68
        return $def;
1041 68
    }
1042 68
1043
    /**
1044
     * {@inheritdoc}
1045
     */
1046
    public function createSchemaTable()
1047
    {
1048
        // Create the public/custom schema if it doesn't already exist
1049
        if ($this->hasSchema($this->getSchemaName()) === false) {
1050 68
            $this->createSchema($this->getSchemaName());
1051
        }
1052 68
1053
        $this->fetchAll(sprintf('SET search_path TO %s', $this->getSchemaName()));
1054
1055 68
        parent::createSchemaTable();
1056
    }
1057 68
1058 68
    /**
1059 68
     * Creates the specified schema.
1060
     *
1061
     * @param  string $schemaName Schema Name
1062
     * @return void
1063
     */
1064
    public function createSchema($schemaName = 'public')
1065
    {
1066
        $sql = sprintf('CREATE SCHEMA %s;', $this->quoteSchemaName($schemaName)); // from postgres 9.3 we can use "CREATE SCHEMA IF NOT EXISTS schema_name"
1067
        $this->execute($sql);
1068 68
    }
1069
1070 68
    /**
1071 68
     * Checks to see if a schema exists.
1072 68
     *
1073
     * @param string $schemaName Schema Name
1074
     * @return bool
1075
     */
1076
    public function hasSchema($schemaName)
1077
    {
1078
        $sql = sprintf(
1079 68
            "SELECT count(*)
1080
             FROM pg_namespace
1081 68
             WHERE nspname = '%s'",
1082 68
            $schemaName
1083 68
        );
1084 68
        $result = $this->fetchRow($sql);
1085
1086
        return $result['count'] > 0;
1087
    }
1088
1089
    /**
1090
     * Drops the specified schema table.
1091 68
     *
1092
     * @param string $schemaName Schema name
1093
     * @return void
1094
     */
1095 68
    public function dropSchema($schemaName)
1096 68
    {
1097 68
        $sql = sprintf("DROP SCHEMA IF EXISTS %s CASCADE;", $this->quoteSchemaName($schemaName));
1098 68
        $this->execute($sql);
1099 68
    }
1100 68
1101 68
    /**
1102
     * Drops all schemas.
1103
     *
1104
     * @return void
1105
     */
1106
    public function dropAllSchemas()
1107 73
    {
1108
        foreach ($this->getAllSchemas() as $schema) {
1109 73
            $this->dropSchema($schema);
1110
        }
1111
    }
1112
1113
    /**
1114
     * Returns schemas.
1115 73
     *
1116
     * @return array
1117
     */
1118 73
    public function getAllSchemas()
1119
    {
1120
        $sql = "SELECT schema_name
1121
                FROM information_schema.schemata
1122
                WHERE schema_name <> 'information_schema' AND schema_name !~ '^pg_'";
1123
        $items = $this->fetchAll($sql);
1124
        $schemaNames = [];
1125
        foreach ($items as $item) {
1126
            $schemaNames[] = $item['schema_name'];
1127 14
        }
1128
1129 14
        return $schemaNames;
1130 1
    }
1131
1132
    /**
1133 13
     * {@inheritdoc}
1134 13
     */
1135
    public function getColumnTypes()
1136
    {
1137
        return array_merge(parent::getColumnTypes(), ['json', 'jsonb', 'cidr', 'inet', 'macaddr', 'interval']);
1138
    }
1139
1140
    /**
1141
     * {@inheritdoc}
1142 68
     */
1143
    public function isValidColumnType(Column $column)
1144 68
    {
1145 68
        // If not a standard column type, maybe it is array type?
1146
        return (parent::isValidColumnType($column) || $this->isArrayType($column->getType()));
0 ignored issues
show
Bug introduced by
It seems like $column->getType() targeting Phinx\Db\Table\Column::getType() can also be of type object<Phinx\Util\Literal>; however, Phinx\Db\Adapter\PostgresAdapter::isArrayType() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1147
    }
1148
1149
    /**
1150
     * Check if the given column is an array of a valid type.
1151 68
     *
1152
     * @param  string $columnType
1153 68
     * @return bool
1154
     */
1155
    protected function isArrayType($columnType)
1156
    {
1157
        if (!preg_match('/^([a-z]+)(?:\[\]){1,}$/', $columnType, $matches)) {
1158
            return false;
1159
        }
1160
1161
        $baseType = $matches[1];
1162
1163
        return in_array($baseType, $this->getColumnTypes());
1164
    }
1165
1166
    /**
1167
     * Gets the schema name.
1168
     *
1169
     * @return string
1170
     */
1171
    private function getSchemaName()
1172
    {
1173
        $options = $this->getOptions();
1174
1175
        return empty($options['schema']) ? 'public' : $options['schema'];
1176
    }
1177
1178
    /**
1179
     * {@inheritdoc}
1180
     */
1181
    public function castToBool($value)
1182
    {
1183
        return (bool)$value ? 'TRUE' : 'FALSE';
1184
    }
1185
}
1186