Completed
Pull Request — master (#1327)
by
unknown
02:04
created

PostgresAdapter::connect()   C

Complexity

Conditions 8
Paths 10

Size

Total Lines 42
Code Lines 24

Duplication

Lines 5
Ratio 11.9 %

Code Coverage

Tests 21
CRAP Score 8

Importance

Changes 0
Metric Value
dl 5
loc 42
ccs 21
cts 21
cp 1
rs 5.3846
c 0
b 0
f 0
cc 8
eloc 24
nc 10
nop 0
crap 8
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
36
class PostgresAdapter extends PdoAdapter implements AdapterInterface
37
{
38
    const INT_SMALL = 65535;
39
40
    /**
41
     * Columns with comments
42
     *
43
     * @var array
44
     */
45
    protected $columnsWithComments = [];
46
47
    /**
48
     * {@inheritdoc}
49
     */
50 68
    public function connect()
51
    {
52 68
        if ($this->connection === null) {
53 68
            if (!class_exists('PDO') || !in_array('pgsql', \PDO::getAvailableDrivers(), true)) {
54
                // @codeCoverageIgnoreStart
55
                throw new \RuntimeException('You need to enable the PDO_Pgsql extension for Phinx to run properly.');
56
                // @codeCoverageIgnoreEnd
57
            }
58
59 68
            $db = null;
60 68
            $options = $this->getOptions();
61
62
            // if port is specified use it, otherwise use the PostgreSQL default
63 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...
64 68
                $dsn = 'pgsql:host=' . $options['host'] . ';port=' . $options['port'] . ';dbname=' . $options['name'];
65 68
            } else {
66 1
                $dsn = 'pgsql:host=' . $options['host'] . ';dbname=' . $options['name'];
67
            }
68
69
            try {
70 68
                $db = new \PDO($dsn, $options['user'], $options['pass'], [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]);
71 68
            } catch (\PDOException $exception) {
72 1
                throw new \InvalidArgumentException(sprintf(
73 1
                    'There was a problem connecting to the database: %s',
74 1
                    $exception->getMessage()
75 1
                ));
76
            }
77
78 68
            try {
79 68
                if (isset($options['schema'])) {
80 68
                    $this->execute('SET search_path TO '.$options['schema']);
81
                }
82
            } catch (\PDOException $exception) {
83
                throw new \InvalidArgumentException(sprintf(
84
                    'Schema does not exists: %s',
85 68
                    $exception->getMessage()
86
                ));
87 68
            }
88 68
89
            $this->setConnection($db);
90
        }
91
    }
92
93
    /**
94
     * {@inheritdoc}
95
     */
96
    public function disconnect()
97
    {
98
        $this->connection = null;
99
    }
100
101
    /**
102
     * {@inheritdoc}
103
     */
104
    public function hasTransactions()
105
    {
106
        return true;
107
    }
108
109
    /**
110
     * {@inheritdoc}
111
     */
112
    public function beginTransaction()
113
    {
114
        $this->execute('BEGIN');
115
    }
116
117
    /**
118
     * {@inheritdoc}
119
     */
120
    public function commitTransaction()
121
    {
122
        $this->execute('COMMIT');
123
    }
124
125
    /**
126
     * {@inheritdoc}
127
     */
128 68
    public function rollbackTransaction()
129
    {
130 68
        $this->execute('ROLLBACK');
131
    }
132
133
    /**
134
     * Quotes a schema name for use in a query.
135
     *
136 68
     * @param string $schemaName Schema Name
137
     * @return string
138 68
     */
139
    public function quoteSchemaName($schemaName)
140
    {
141
        return $this->quoteColumnName($schemaName);
142
    }
143
144 68
    /**
145
     * {@inheritdoc}
146 68
     */
147
    public function quoteTableName($tableName)
148
    {
149
        return $this->quoteSchemaName($this->getSchemaName()) . '.' . $this->quoteColumnName($tableName);
150
    }
151
152 68
    /**
153
     * {@inheritdoc}
154 68
     */
155 68
    public function quoteColumnName($columnName)
156
    {
157
        return '"' . $columnName . '"';
158
    }
159 68
160 68
    /**
161 68
     * {@inheritdoc}
162 68
     */
163 68
    public function hasTable($tableName)
164
    {
165 68
        $result = $this->getConnection()->query(
166
            sprintf(
167
                'SELECT *
168
                FROM information_schema.tables
169
                WHERE table_schema = %s
170
                AND lower(table_name) = lower(%s)',
171 68
                $this->getConnection()->quote($this->getSchemaName()),
172
                $this->getConnection()->quote($tableName)
173 68
            )
174
        );
175
176 68
        return $result->rowCount() === 1;
177 68
    }
178 48
179 48
    /**
180 48
     * {@inheritdoc}
181 48
     */
182
    public function createTable(Table $table)
183 48
    {
184 48
        $options = $table->getOptions();
185 68
186
         // Add the default primary key
187 2
        $columns = $table->getPendingColumns();
188 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...
189 2
            $column = new Column();
190 2
            $column->setName('id')
191
                   ->setType('integer')
192 2
                   ->setIdentity(true);
193 2
194 2
            array_unshift($columns, $column);
195
            $options['primary_key'] = 'id';
196
        } elseif (isset($options['id']) && is_string($options['id'])) {
197 68
            // Handle id => "field_name" to support AUTO_INCREMENT
198 68
            $column = new Column();
199
            $column->setName($options['id'])
200 68
                   ->setType('integer')
201 68
                   ->setIdentity(true);
202 68
203
            array_unshift($columns, $column);
204
            $options['primary_key'] = $options['id'];
205 68
        }
206 6
207 6
        // TODO - process table options like collation etc
208 68
        $sql = 'CREATE TABLE ';
209
        $sql .= $this->quoteTableName($table->getName()) . ' (';
210
211 68
        $this->columnsWithComments = [];
212 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...
213 68
            $sql .= $this->quoteColumnName($column->getName()) . ' ' . $this->getColumnSqlDefinition($column) . ', ';
214 68
215 68
            // set column comments, if needed
216 68
            if ($column->getComment()) {
217
                $this->columnsWithComments[] = $column;
218
            }
219 1
        }
220 1
221 1
         // set the primary key(s)
222 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...
223 1
            $sql = rtrim($sql);
224 1
            $sql .= sprintf(' CONSTRAINT %s_pkey PRIMARY KEY (', $table->getName());
225 1
            if (is_string($options['primary_key'])) { // handle primary_key => 'id'
226 1
                $sql .= $this->quoteColumnName($options['primary_key']);
227 1
            } 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...
228 1
                $sql .= implode(',', array_map([$this, 'quoteColumnName'], $options['primary_key']));
229 68
            }
230 68
            $sql .= ')';
231 2
        } else {
232
            $sql = substr(rtrim($sql), 0, -1); // no primary keys
233
        }
234
235 68
        // set the foreign keys
236 68
        $foreignKeys = $table->getForeignKeys();
237 1
        if (!empty($foreignKeys)) {
238 1
            foreach ($foreignKeys as $foreignKey) {
239 1
                $sql .= ', ' . $this->getForeignKeySqlDefinition($foreignKey, $table->getName());
240 1
            }
241
        }
242 68
243
        $sql .= ');';
244
245 68
        // process column comments
246 6
        if (!empty($this->columnsWithComments)) {
247 6
            foreach ($this->columnsWithComments as $column) {
248 6
                $sql .= $this->getColumnCommentSqlDefinition($column, $table->getName());
249 6
            }
250
        }
251
252
        // set the indexes
253 68
        $indexes = $table->getIndexes();
254 68
        if (!empty($indexes)) {
255 5
            foreach ($indexes as $index) {
256 5
                $sql .= $this->getIndexSqlDefinition($index, $table->getName());
257 5
            }
258 5
        }
259
260
        // execute the sql
261 68
        $this->execute($sql);
262
263
        // process table comments
264 68
        if (isset($options['comment'])) {
265 1
            $sql = sprintf(
266 1
                'COMMENT ON TABLE %s IS %s',
267 1
                $this->quoteTableName($table->getName()),
268 1
                $this->getConnection()->quote($options['comment'])
269 1
            );
270 1
            $this->execute($sql);
271 1
        }
272 68
    }
273
274
    /**
275
     * {@inheritdoc}
276
     */
277 1
    public function renameTable($tableName, $newTableName)
278
    {
279 1
        $sql = sprintf(
280 1
            'ALTER TABLE %s RENAME TO %s',
281 1
            $this->quoteTableName($tableName),
282 1
            $this->quoteColumnName($newTableName)
283 1
        );
284 1
        $this->execute($sql);
285 1
    }
286
287
    /**
288
     * {@inheritdoc}
289
     */
290 1
    public function dropTable($tableName)
291
    {
292 1
        $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tableName)));
293 1
    }
294
295
    /**
296
     * {@inheritdoc}
297
     */
298 1
    public function truncateTable($tableName)
299
    {
300 1
        $sql = sprintf(
301 1
            'TRUNCATE TABLE %s',
302 1
            $this->quoteTableName($tableName)
303 1
        );
304
305 1
        $this->execute($sql);
306 1
    }
307
308
    /**
309
     * {@inheritdoc}
310
     */
311 9 View Code Duplication
    public function getColumns($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...
312
    {
313 9
        $columns = [];
314 9
        $sql = sprintf(
315
            "SELECT column_name, data_type, is_identity, is_nullable,
316
             column_default, character_maximum_length, numeric_precision, numeric_scale
317
             FROM information_schema.columns
318 9
             WHERE table_name ='%s'",
319
            $tableName
320 9
        );
321 9
        $columnsInfo = $this->fetchAll($sql);
322
323 9
        foreach ($columnsInfo as $columnInfo) {
324 9
            $column = new Column();
325 9
            $column->setName($columnInfo['column_name'])
326 9
                   ->setType($this->getPhinxType($columnInfo['data_type']))
0 ignored issues
show
Bug introduced by
It seems like $this->getPhinxType($columnInfo['data_type']) targeting Phinx\Db\Adapter\PostgresAdapter::getPhinxType() can also be of type array<string,string|inte...ng","limit":"integer"}>; however, Phinx\Db\Table\Column::setType() 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...
327 9
                   ->setNull($columnInfo['is_nullable'] === 'YES')
328 9
                   ->setDefault($columnInfo['column_default'])
329 9
                   ->setIdentity($columnInfo['is_identity'] === 'YES')
330 9
                   ->setPrecision($columnInfo['numeric_precision'])
331 9
                   ->setScale($columnInfo['numeric_scale']);
332
333 9
            if (preg_match('/\bwith time zone$/', $columnInfo['data_type'])) {
334 1
                $column->setTimezone(true);
335 1
            }
336
337 9
            if (isset($columnInfo['character_maximum_length'])) {
338 5
                $column->setLimit($columnInfo['character_maximum_length']);
339 5
            }
340 9
            $columns[] = $column;
341 9
        }
342 9
343
        return $columns;
344
    }
345
346
    /**
347
     * {@inheritdoc}
348 24
     */
349 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...
350 24
    {
351
        $sql = sprintf(
352
            "SELECT count(*)
353 24
            FROM information_schema.columns
354 24
            WHERE table_schema = '%s' AND table_name = '%s' AND column_name = '%s'",
355 24
            $this->getSchemaName(),
356
            $tableName,
357 24
            $columnName
358
        );
359 24
360 24
        $result = $this->fetchRow($sql);
361
362
        return $result['count'] > 0;
363
    }
364
365
    /**
366 18
     * {@inheritdoc}
367
     */
368 18 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...
369 18
    {
370 18
        $sql = sprintf(
371 18
            'ALTER TABLE %s ADD %s %s;',
372 18
            $this->quoteTableName($table->getName()),
373 18
            $this->quoteColumnName($column->getName()),
374
            $this->getColumnSqlDefinition($column)
375 18
        );
376 18
377
        if ($column->getComment()) {
378
            $sql .= $this->getColumnCommentSqlDefinition($column, $table->getName());
379
        }
380
381 3
        $this->execute($sql);
382
    }
383 3
384
    /**
385
     * {@inheritdoc}
386 3
     */
387 3
    public function renameColumn($tableName, $columnName, $newColumnName)
388
    {
389 3
        $sql = sprintf(
390 3
            "SELECT CASE WHEN COUNT(*) > 0 THEN 1 ELSE 0 END AS column_exists
391 3
             FROM information_schema.columns
392 1
             WHERE table_name ='%s' AND column_name = '%s'",
393
            $tableName,
394 2
            $columnName
395 2
        );
396 2
        $result = $this->fetchRow($sql);
397 2
        if (!(bool)$result['column_exists']) {
398 2
            throw new \InvalidArgumentException("The specified column does not exist: $columnName");
399 2
        }
400 2
        $this->execute(
401 2
            sprintf(
402 2
                'ALTER TABLE %s RENAME COLUMN %s TO %s',
403
                $this->quoteTableName($tableName),
404
                $this->quoteColumnName($columnName),
405
                $this->quoteColumnName($newColumnName)
406
            )
407 5
        );
408
    }
409
410
    /**
411 5
     * {@inheritdoc}
412 5
     */
413 5
    public function changeColumn($tableName, $columnName, Column $newColumn)
414 5
    {
415 5
        // TODO - is it possible to merge these 3 queries into less?
416 5
        // change data type
417
        $sql = sprintf(
418 5
            'ALTER TABLE %s ALTER COLUMN %s TYPE %s',
419 5
            $this->quoteTableName($tableName),
420
            $this->quoteColumnName($columnName),
421 5
            $this->getColumnSqlDefinition($newColumn)
422 5
        );
423
        //NULL and DEFAULT cannot be set while changing column type
424 5
        $sql = preg_replace('/ NOT NULL/', '', $sql);
425 5
        $sql = preg_replace('/ NULL/', '', $sql);
426 5
        //If it is set, DEFAULT is the last definition
427 5
        $sql = preg_replace('/DEFAULT .*/', '', $sql);
428 5
        $this->execute($sql);
429 5
        // process null
430 2
        $sql = sprintf(
431 2
            'ALTER TABLE %s ALTER COLUMN %s',
432 4
            $this->quoteTableName($tableName),
433
            $this->quoteColumnName($columnName)
434 5
        );
435 5
        if ($newColumn->isNull()) {
436
            $sql .= ' DROP NOT NULL';
437 1
        } else {
438 1
            $sql .= ' SET NOT NULL';
439 1
        }
440 1
        $this->execute($sql);
441 1
        if (!is_null($newColumn->getDefault())) {
442 1
            //change default
443 1
            $this->execute(
444 1
                sprintf(
445 1
                    'ALTER TABLE %s ALTER COLUMN %s SET %s',
446
                    $this->quoteTableName($tableName),
447 4
                    $this->quoteColumnName($columnName),
448 4
                    $this->getDefaultValueDefinition($newColumn->getDefault())
449 4
                )
450 4
            );
451 4
        } else {
452 4
            //drop default
453 4
            $this->execute(
454
                sprintf(
455
                    'ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT',
456 5
                    $this->quoteTableName($tableName),
457 1
                    $this->quoteColumnName($columnName)
458 1
                )
459 1
            );
460 1
        }
461 1
        // rename column
462 1
        if ($columnName !== $newColumn->getName()) {
463 1
            $this->execute(
464 1
                sprintf(
465 1
                    'ALTER TABLE %s RENAME COLUMN %s TO %s',
466
                    $this->quoteTableName($tableName),
467
                    $this->quoteColumnName($columnName),
468 5
                    $this->quoteColumnName($newColumn->getName())
469 2
                )
470 2
            );
471 2
        }
472 5
473
        // change column comment if needed
474
        if ($newColumn->getComment()) {
475
            $sql = $this->getColumnCommentSqlDefinition($newColumn, $tableName);
476
            $this->execute($sql);
477 1
        }
478
    }
479 1
480 1
    /**
481 1
     * {@inheritdoc}
482 1
     */
483 1
    public function dropColumn($tableName, $columnName)
484 1
    {
485 1
        $this->execute(
486 1
            sprintf(
487
                'ALTER TABLE %s DROP COLUMN %s',
488
                $this->quoteTableName($tableName),
489
                $this->quoteColumnName($columnName)
490
            )
491
        );
492
    }
493
494 9
    /**
495
     * Get an array of indexes from a particular table.
496 9
     *
497
     * @param string $tableName Table Name
498
     * @return array
499
     */
500 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...
501
    {
502
        $indexes = [];
503
        $sql = "SELECT
504
            i.relname AS index_name,
505
            a.attname AS column_name
506
        FROM
507
            pg_class t,
508
            pg_class i,
509
            pg_index ix,
510
            pg_attribute a
511
        WHERE
512
            t.oid = ix.indrelid
513
            AND i.oid = ix.indexrelid
514 9
            AND a.attrelid = t.oid
515 9
            AND a.attnum = ANY(ix.indkey)
516 9
            AND t.relkind = 'r'
517 9
            AND t.relname = '$tableName'
518 9
        ORDER BY
519 9
            t.relname,
520 9
            i.relname;";
521 9
        $rows = $this->fetchAll($sql);
522 9
        foreach ($rows as $row) {
523
            if (!isset($indexes[$row['index_name']])) {
524
                $indexes[$row['index_name']] = ['columns' => []];
525
            }
526
            $indexes[$row['index_name']]['columns'][] = strtolower($row['column_name']);
527
        }
528 9
529
        return $indexes;
530 9
    }
531 4
532 4
    /**
533 9
     * {@inheritdoc}
534 9
     */
535 9 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...
536 9
    {
537 9
        if (is_string($columns)) {
538
            $columns = [$columns];
539 8
        }
540 8
        $columns = array_map('strtolower', $columns);
541
        $indexes = $this->getIndexes($tableName);
542
        foreach ($indexes as $index) {
543
            if (array_diff($index['columns'], $columns) === array_diff($columns, $index['columns'])) {
544
                return true;
545
            }
546 1
        }
547
548 1
        return false;
549 1
    }
550 1
551 1
    /**
552
     * {@inheritdoc}
553
     */
554 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...
555
    {
556
        $indexes = $this->getIndexes($tableName);
557
        foreach ($indexes as $name => $index) {
558
            if ($name === $indexName) {
559
                return true;
560 2
            }
561
        }
562 2
563 2
        return false;
564 2
    }
565
566
    /**
567
     * {@inheritdoc}
568
     */
569 1
    public function addIndex(Table $table, Index $index)
570
    {
571 1
        $sql = $this->getIndexSqlDefinition($index, $table->getName());
572 1
        $this->execute($sql);
573 1
    }
574
575 1
    /**
576 1
     * {@inheritdoc}
577
     */
578 1
    public function dropIndex($tableName, $columns)
579 1
    {
580 1
        if (is_string($columns)) {
581 1
            $columns = [$columns]; // str to array
582 1
        }
583 1
584 1
        $indexes = $this->getIndexes($tableName);
585 1
        $columns = array_map('strtolower', $columns);
586 1
587
        foreach ($indexes as $indexName => $index) {
588 1
            $a = array_diff($columns, $index['columns']);
589
            if (empty($a)) {
590
                $this->execute(
591
                    sprintf(
592
                        'DROP INDEX IF EXISTS %s',
593
                        $this->quoteColumnName($indexName)
594
                    )
595
                );
596 1
597
                return;
598 1
            }
599 1
        }
600
    }
601 1
602 1
    /**
603 1
     * {@inheritdoc}
604
     */
605
    public function dropIndexByName($tableName, $indexName)
606
    {
607
        $sql = sprintf(
608 3
            'DROP INDEX IF EXISTS %s',
609
            $indexName
610 3
        );
611 1
        $this->execute($sql);
612 1
    }
613 3
614 3
    /**
615
     * {@inheritdoc}
616
     */
617 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...
618
    {
619
        if (is_string($columns)) {
620 3
            $columns = [$columns]; // str to array
621 3
        }
622 3
        $foreignKeys = $this->getForeignKeys($tableName);
623 3
        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...
624
            if (isset($foreignKeys[$constraint])) {
625 1
                return !empty($foreignKeys[$constraint]);
626 1
            }
627
628
            return false;
629
        } else {
630
            foreach ($foreignKeys as $key) {
631
                $a = array_diff($columns, $key['columns']);
632
                if (empty($a)) {
633
                    return true;
634
                }
635
            }
636 3
637
            return false;
638 3
        }
639 3
    }
640
641
    /**
642
     * Get an array of foreign keys from a particular table.
643
     *
644
     * @param string $tableName Table Name
645
     * @return array
646
     */
647 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...
648
    {
649
        $foreignKeys = [];
650 3
        $rows = $this->fetchAll(sprintf(
651
            "SELECT
652 3
                    tc.constraint_name,
653 3
                    tc.table_name, kcu.column_name,
654 3
                    ccu.table_name AS referenced_table_name,
655 3
                    ccu.column_name AS referenced_column_name
656 3
                FROM
657 3
                    information_schema.table_constraints AS tc
658 3
                    JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
659 3
                    JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
660
                WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name = '%s'
661
                ORDER BY kcu.position_in_unique_constraint",
662
            $tableName
663
        ));
664
        foreach ($rows as $row) {
665 2
            $foreignKeys[$row['constraint_name']]['table'] = $row['table_name'];
666
            $foreignKeys[$row['constraint_name']]['columns'][] = $row['column_name'];
667 2
            $foreignKeys[$row['constraint_name']]['referenced_table'] = $row['referenced_table_name'];
668 2
            $foreignKeys[$row['constraint_name']]['referenced_columns'][] = $row['referenced_column_name'];
669 2
        }
670 2
671 2
        return $foreignKeys;
672 2
    }
673 2
674
    /**
675
     * {@inheritdoc}
676
     */
677 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...
678 1
    {
679
        $sql = sprintf(
680 1
            'ALTER TABLE %s ADD %s',
681
            $this->quoteTableName($table->getName()),
682
            $this->getForeignKeySqlDefinition($foreignKey, $table->getName())
683
        );
684 1
        $this->execute($sql);
685 1
    }
686 1
687 1
    /**
688 1
     * {@inheritdoc}
689
     */
690 1 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...
691 1
    {
692 1
        if (is_string($columns)) {
693 1
            $columns = [$columns]; // str to array
694 1
        }
695
696
        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...
697
            $this->execute(
698
                sprintf(
699
                    'ALTER TABLE %s DROP CONSTRAINT %s',
700
                    $this->quoteTableName($tableName),
701 1
                    $constraint
702 1
                )
703
            );
704 1
        } else {
705
            foreach ($columns as $column) {
706 1
                $rows = $this->fetchAll(sprintf(
707 1
                    "SELECT CONSTRAINT_NAME
708 1
                      FROM information_schema.KEY_COLUMN_USAGE
709 1
                      WHERE TABLE_SCHEMA = CURRENT_SCHEMA()
710
                        AND TABLE_NAME IS NOT NULL
711 1
                        AND TABLE_NAME = '%s'
712
                        AND COLUMN_NAME = '%s'
713
                      ORDER BY POSITION_IN_UNIQUE_CONSTRAINT",
714
                    $tableName,
715
                    $column
716 68
                ));
717
718
                foreach ($rows as $row) {
719 68
                    $this->dropForeignKey($tableName, $columns, $row['constraint_name']);
720 14
                }
721
            }
722 1
        }
723
    }
724 1
725
    /**
726 14
     * {@inheritdoc}
727 68
     */
728 68
    public function getSqlType($type, $limit = null)
729 68
    {
730 68
        switch ($type) {
731 68
            case static::PHINX_TYPE_TEXT:
732 68
            case static::PHINX_TYPE_TIME:
733 68
            case static::PHINX_TYPE_DATE:
734 68
            case static::PHINX_TYPE_BOOLEAN:
735 68
            case static::PHINX_TYPE_JSON:
736 68
            case static::PHINX_TYPE_JSONB:
737 68
            case static::PHINX_TYPE_UUID:
738 68
            case static::PHINX_TYPE_CIDR:
739 2
            case static::PHINX_TYPE_INET:
740 68
            case static::PHINX_TYPE_MACADDR:
741 68
            case static::PHINX_TYPE_TIMESTAMP:
742 68
                return ['name' => $type];
743
            case static::PHINX_TYPE_INTEGER:
744 68
                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...
745 68
                    return [
746 68
                        'name' => 'smallint',
747 1
                        'limit' => static::INT_SMALL
748 68
                    ];
749 68
                }
750 68
751 15
                return ['name' => $type];
752 15
            case static::PHINX_TYPE_DECIMAL:
753 1
                return ['name' => $type, 'precision' => 18, 'scale' => 0];
754
            case static::PHINX_TYPE_STRING:
755
                return ['name' => 'character varying', 'limit' => 255];
756
            case static::PHINX_TYPE_CHAR:
757
                return ['name' => 'character', 'limit' => 255];
758 14
            case static::PHINX_TYPE_BIG_INTEGER:
759
                return ['name' => 'bigint'];
760
            case static::PHINX_TYPE_FLOAT:
761 14
                return ['name' => 'real'];
762
            case static::PHINX_TYPE_DATETIME:
763
                return ['name' => 'timestamp'];
764 14
            case static::PHINX_TYPE_BLOB:
765
            case static::PHINX_TYPE_BINARY:
766
                return ['name' => 'bytea'];
767 14
            case static::PHINX_TYPE_INTERVAL:
768
                return ['name' => 'interval'];
769
            // Geospatial database types
770 14
            // Spatial storage in Postgres is done via the PostGIS extension,
771 14
            // which enables the use of the "geography" type in combination
772 13
            // with SRID 4326.
773
            case static::PHINX_TYPE_GEOMETRY:
774
                return ['name' => 'geography', 'type' => 'geometry', 'srid' => 4326];
775 1
            case static::PHINX_TYPE_POINT:
776 14
                return ['name' => 'geography', 'type' => 'point', 'srid' => 4326];
777
            case static::PHINX_TYPE_LINESTRING:
778
                return ['name' => 'geography', 'type' => 'linestring', 'srid' => 4326];
779
            case static::PHINX_TYPE_POLYGON:
780
                return ['name' => 'geography', 'type' => 'polygon', 'srid' => 4326];
781
            default:
782
                if ($this->isArrayType($type)) {
783
                    return ['name' => $type];
784
                }
785 10
                // Return array type
786
                throw new \RuntimeException('The type: "' . $type . '" is not supported');
787
        }
788 10
    }
789 10
790 6
    /**
791 10
     * Returns Phinx type by SQL type
792 10
     *
793
     * @param string $sqlType SQL type
794 10
     * @returns string Phinx type
795 2
     */
796 10
    public function getPhinxType($sqlType)
797
    {
798 10
        switch ($sqlType) {
799
            case 'character varying':
800 10
            case 'varchar':
801
                return static::PHINX_TYPE_STRING;
802 1
            case 'character':
803
            case 'char':
804 1
                return static::PHINX_TYPE_CHAR;
805 10
            case 'text':
806 10
                return static::PHINX_TYPE_TEXT;
807 10
            case 'json':
808 9
                return static::PHINX_TYPE_JSON;
809 5
            case 'jsonb':
810 5
                return static::PHINX_TYPE_JSONB;
811 3
            case 'smallint':
812 4
                return [
813 4
                    'name' => 'smallint',
814 2
                    'limit' => static::INT_SMALL
815 4
                ];
816 4
            case 'int':
817 2
            case 'int4':
818 4
            case 'integer':
819 1
                return static::PHINX_TYPE_INTEGER;
820
            case 'decimal':
821 4
            case 'numeric':
822 4
                return static::PHINX_TYPE_DECIMAL;
823 4
            case 'bigint':
824 4
            case 'int8':
825 3
                return static::PHINX_TYPE_BIG_INTEGER;
826 4
            case 'real':
827 2
            case 'float4':
828 4
                return static::PHINX_TYPE_FLOAT;
829 4
            case 'bytea':
830 4
                return static::PHINX_TYPE_BINARY;
831 4
            case 'interval':
832 3
                return static::PHINX_TYPE_INTERVAL;
833 3
            case 'time':
834 3
            case 'timetz':
835 3
            case 'time with time zone':
836 1
            case 'time without time zone':
837 1
                return static::PHINX_TYPE_TIME;
838
            case 'date':
839
                return static::PHINX_TYPE_DATE;
840
            case 'timestamp':
841
            case 'timestamptz':
842
            case 'timestamp with time zone':
843
            case 'timestamp without time zone':
844
                return static::PHINX_TYPE_DATETIME;
845
            case 'bool':
846
            case 'boolean':
847
                return static::PHINX_TYPE_BOOLEAN;
848
            case 'uuid':
849
                return static::PHINX_TYPE_UUID;
850
            case 'cidr':
851
                return static::PHINX_TYPE_CIDR;
852 1
            case 'inet':
853
                return static::PHINX_TYPE_INET;
854 1
            case 'macaddr':
855 1
                return static::PHINX_TYPE_MACADDR;
856 1
            default:
857
                throw new \RuntimeException('The PostgreSQL type: "' . $sqlType . '" is not supported');
858
        }
859
    }
860
861 2
    /**
862
     * {@inheritdoc}
863 2
     */
864 2
    public function createDatabase($name, $options = [])
865 2
    {
866
        $charset = isset($options['charset']) ? $options['charset'] : 'utf8';
867
        $this->execute(sprintf("CREATE DATABASE %s WITH ENCODING = '%s'", $name, $charset));
868
    }
869
870
    /**
871 1
     * {@inheritdoc}
872
     */
873 1
    public function hasDatabase($databaseName)
874 1
    {
875 1
        $sql = sprintf("SELECT count(*) FROM pg_database WHERE datname = '%s'", $databaseName);
876 1
        $result = $this->fetchRow($sql);
877
878
        return $result['count'] > 0;
879
    }
880
881
    /**
882
     * {@inheritdoc}
883
     */
884 68
    public function dropDatabase($name)
885
    {
886 68
        $this->disconnect();
887 4
        $this->execute(sprintf('DROP DATABASE IF EXISTS %s', $name));
888 68
        $this->connect();
889 68
    }
890 68
891 68
    /**
892
     * Get the defintion for a `DEFAULT` statement.
893
     *
894
     * @param  mixed $default
895
     * @return string
896
     */
897 View Code Duplication
    protected function getDefaultValueDefinition($default)
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...
898
    {
899
        if (is_string($default) && 'CURRENT_TIMESTAMP' !== $default) {
900 68
            $default = $this->getConnection()->quote($default);
901
        } elseif (is_bool($default)) {
902 68
            $default = $this->castToBool($default);
903 68
        }
904 50
905 50
        return isset($default) ? 'DEFAULT ' . $default : '';
906 68
    }
907 68
908
    /**
909 68
     * Gets the PostgreSQL Column Definition for a Column object.
910 1
     *
911 1
     * @param \Phinx\Db\Table\Column $column Column
912 1
     * @return string
913 1
     */
914 1
    protected function getColumnSqlDefinition(Column $column)
915 68
    {
916
        $buffer = [];
917
        if ($column->isIdentity()) {
918
            $buffer[] = $column->getType() == 'biginteger' ? 'BIGSERIAL' : 'SERIAL';
919
        } else {
920
            $sqlType = $this->getSqlType($column->getType(), $column->getLimit());
921
            $buffer[] = strtoupper($sqlType['name']);
922 68
            // integers cant have limits in postgres
923 68
            if (static::PHINX_TYPE_DECIMAL === $sqlType['name'] && ($column->getPrecision() || $column->getScale())) {
924 68
                $buffer[] = sprintf(
925 68
                    '(%s, %s)',
926 68
                    $column->getPrecision() ?: $sqlType['precision'],
927
                    $column->getScale() ?: $sqlType['scale']
928
                );
929 68
            } elseif (in_array($sqlType['name'], ['geography'])) {
930 68
                // geography type must be written with geometry type and srid, like this: geography(POLYGON,4326)
931 68
                $buffer[] = sprintf(
932 68
                    '(%s,%s)',
933 1
                    strtoupper($sqlType['type']),
934 1
                    $sqlType['srid']
935
                );
936
            } elseif (!in_array($sqlType['name'], ['integer', 'smallint', 'bigint'])) {
937 68
                if ($column->getLimit() || isset($sqlType['limit'])) {
938
                    $buffer[] = sprintf('(%s)', $column->getLimit() ?: $sqlType['limit']);
939 68
                }
940 68
            }
941 68
942
            $timeTypes = [
943 68
                'time',
944
                'timestamp',
945
            ];
946
            if (in_array($sqlType['name'], $timeTypes) && $column->isTimezone()) {
947
                $buffer[] = strtoupper('with time zone');
948
            }
949
        }
950
951
        $buffer[] = $column->isNull() ? 'NULL' : 'NOT NULL';
952
953 6
        if (!is_null($column->getDefault())) {
954
            $buffer[] = $this->getDefaultValueDefinition($column->getDefault());
955
        }
956 6
957 6
        return implode(' ', $buffer);
958 6
    }
959
960 6
    /**
961 6
     * Gets the PostgreSQL Column Comment Defininition for a column object.
962 6
     *
963 6
     * @param \Phinx\Db\Table\Column $column Column
964
     * @param string $tableName Table name
965 6
     * @return string
966
     */
967
    protected function getColumnCommentSqlDefinition(Column $column, $tableName)
968
    {
969
        // passing 'null' is to remove column comment
970
        $comment = (strcasecmp($column->getComment(), 'NULL') !== 0)
971
                 ? $this->getConnection()->quote($column->getComment())
972
                 : 'NULL';
973
974
        return sprintf(
975 7
            'COMMENT ON COLUMN %s.%s IS %s;',
976
            $this->quoteSchemaName($tableName),
977 7
            $this->quoteColumnName($column->getName()),
978 3
            $comment
979 3
        );
980 5
    }
981 5
982
    /**
983
     * Gets the PostgreSQL Index Definition for an Index object.
984 5
     *
985
     * @param \Phinx\Db\Table\Index  $index Index
986 7
     * @param string $tableName Table name
987 7
     * @return string
988 7
     */
989 7
    protected function getIndexSqlDefinition(Index $index, $tableName)
990 7
    {
991 7 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...
992 7
            $indexName = $index->getName();
993 7
        } else {
994
            $columnNames = $index->getColumns();
995
            if (is_string($columnNames)) {
996
                $columnNames = [$columnNames];
997
            }
998
            $indexName = sprintf('%s_%s', $tableName, implode('_', $columnNames));
999
        }
1000
        $def = sprintf(
1001
            "CREATE %s INDEX %s ON %s (%s);",
1002
            ($index->getType() === Index::UNIQUE ? 'UNIQUE' : ''),
1003 3
            $indexName,
1004
            $this->quoteTableName($tableName),
1005 3
            implode(',', array_map([$this, 'quoteColumnName'], $index->getColumns()))
1006 3
        );
1007 3
1008 3
        return $def;
1009
    }
1010
1011 3
    /**
1012
     * Gets the MySQL Foreign Key Definition for an ForeignKey object.
1013
     *
1014 3
     * @param \Phinx\Db\Table\ForeignKey $foreignKey
1015
     * @param string     $tableName  Table name
1016
     * @return string
1017
     */
1018 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...
1019
    {
1020 68
        $constraintName = $foreignKey->getConstraint() ?: $tableName . '_' . implode('_', $foreignKey->getColumns());
1021
1022
        $def = ' CONSTRAINT "' . $constraintName . '" FOREIGN KEY ("' . implode('", "', $foreignKey->getColumns()) . '")';
1023 68
        $def .= " REFERENCES {$this->quoteTableName($foreignKey->getReferencedTable()->getName())} (\"" . implode('", "', $foreignKey->getReferencedColumns()) . '")';
1024 67
        if ($foreignKey->getOnDelete()) {
1025 67
            $def .= " ON DELETE {$foreignKey->getOnDelete()}";
1026
        }
1027 68
        if ($foreignKey->getOnUpdate()) {
1028
            $def .= " ON UPDATE {$foreignKey->getOnUpdate()}";
1029 68
        }
1030 68
1031
        return $def;
1032
    }
1033
1034
    /**
1035
     * {@inheritdoc}
1036
     */
1037
    public function createSchemaTable()
1038 68
    {
1039
        // Create the public/custom schema if it doesn't already exist
1040 68
        if ($this->hasSchema($this->getSchemaName()) === false) {
1041 68
            $this->createSchema($this->getSchemaName());
1042 68
        }
1043
1044
        $this->fetchAll(sprintf('SET search_path TO %s', $this->getSchemaName()));
1045
1046
        parent::createSchemaTable();
1047
    }
1048
1049
    /**
1050 68
     * Creates the specified schema.
1051
     *
1052 68
     * @param  string $schemaName Schema Name
1053
     * @return void
1054
     */
1055 68
    public function createSchema($schemaName = 'public')
1056
    {
1057 68
        $sql = sprintf('CREATE SCHEMA %s;', $this->quoteSchemaName($schemaName)); // from postgres 9.3 we can use "CREATE SCHEMA IF NOT EXISTS schema_name"
1058 68
        $this->execute($sql);
1059 68
    }
1060
1061
    /**
1062
     * Checks to see if a schema exists.
1063
     *
1064
     * @param string $schemaName Schema Name
1065
     * @return bool
1066
     */
1067
    public function hasSchema($schemaName)
1068 68
    {
1069
        $sql = sprintf(
1070 68
            "SELECT count(*)
1071 68
             FROM pg_namespace
1072 68
             WHERE nspname = '%s'",
1073
            $schemaName
1074
        );
1075
        $result = $this->fetchRow($sql);
1076
1077
        return $result['count'] > 0;
1078
    }
1079 68
1080
    /**
1081 68
     * Drops the specified schema table.
1082 68
     *
1083 68
     * @param string $schemaName Schema name
1084 68
     * @return void
1085
     */
1086
    public function dropSchema($schemaName)
1087
    {
1088
        $sql = sprintf("DROP SCHEMA IF EXISTS %s CASCADE;", $this->quoteSchemaName($schemaName));
1089
        $this->execute($sql);
1090
    }
1091 68
1092
    /**
1093
     * Drops all schemas.
1094
     *
1095 68
     * @return void
1096 68
     */
1097 68
    public function dropAllSchemas()
1098 68
    {
1099 68
        foreach ($this->getAllSchemas() as $schema) {
1100 68
            $this->dropSchema($schema);
1101 68
        }
1102
    }
1103
1104
    /**
1105
     * Returns schemas.
1106
     *
1107 73
     * @return array
1108
     */
1109 73
    public function getAllSchemas()
1110
    {
1111
        $sql = "SELECT schema_name
1112
                FROM information_schema.schemata
1113
                WHERE schema_name <> 'information_schema' AND schema_name !~ '^pg_'";
1114
        $items = $this->fetchAll($sql);
1115 73
        $schemaNames = [];
1116
        foreach ($items as $item) {
1117
            $schemaNames[] = $item['schema_name'];
1118 73
        }
1119
1120
        return $schemaNames;
1121
    }
1122
1123
    /**
1124
     * {@inheritdoc}
1125
     */
1126
    public function getColumnTypes()
1127 14
    {
1128
        return array_merge(parent::getColumnTypes(), ['json', 'jsonb', 'cidr', 'inet', 'macaddr', 'interval']);
1129 14
    }
1130 1
1131
    /**
1132
     * {@inheritdoc}
1133 13
     */
1134 13
    public function isValidColumnType(Column $column)
1135
    {
1136
        // If not a standard column type, maybe it is array type?
1137
        return (parent::isValidColumnType($column) || $this->isArrayType($column->getType()));
1138
    }
1139
1140
    /**
1141
     * Check if the given column is an array of a valid type.
1142 68
     *
1143
     * @param  string $columnType
1144 68
     * @return bool
1145 68
     */
1146
    protected function isArrayType($columnType)
1147
    {
1148
        if (!preg_match('/^([a-z]+)(?:\[\]){1,}$/', $columnType, $matches)) {
1149
            return false;
1150
        }
1151 68
1152
        $baseType = $matches[1];
1153 68
1154
        return in_array($baseType, $this->getColumnTypes());
1155
    }
1156
1157
    /**
1158
     * Gets the schema name.
1159
     *
1160
     * @return string
1161
     */
1162
    private function getSchemaName()
1163
    {
1164
        $options = $this->getOptions();
1165
1166
        return empty($options['schema']) ? 'public' : $options['schema'];
1167
    }
1168
1169
    /**
1170
     * {@inheritdoc}
1171
     */
1172
    public function castToBool($value)
1173
    {
1174
        return (bool)$value ? 'TRUE' : 'FALSE';
1175
    }
1176
}
1177