Test Setup Failed
Pull Request — 2.6 (#3158)
by Ben
05:27
created

PostgreSqlPlatform::getAlterTableSQL()   F

Complexity

Conditions 28
Paths > 20000

Size

Total Lines 133
Code Lines 77

Duplication

Lines 37
Ratio 27.82 %

Code Coverage

Tests 64
CRAP Score 31.0864

Importance

Changes 0
Metric Value
dl 37
loc 133
ccs 64
cts 76
cp 0.8421
rs 2
c 0
b 0
f 0
cc 28
eloc 77
nc 23652
nop 1
crap 31.0864

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\DBAL\Platforms;
21
22
use Doctrine\DBAL\Schema\Column;
23
use Doctrine\DBAL\Schema\ColumnDiff;
24
use Doctrine\DBAL\Schema\Identifier;
25
use Doctrine\DBAL\Schema\Index;
26
use Doctrine\DBAL\Schema\Sequence;
27
use Doctrine\DBAL\Schema\TableDiff;
28
use Doctrine\DBAL\Types\BinaryType;
29
use Doctrine\DBAL\Types\BigIntType;
30
use Doctrine\DBAL\Types\BlobType;
31
use Doctrine\DBAL\Types\IntegerType;
32
use Doctrine\DBAL\Types\Type;
33
34
/**
35
 * PostgreSqlPlatform.
36
 *
37
 * @since  2.0
38
 * @author Roman Borschel <[email protected]>
39
 * @author Lukas Smith <[email protected]> (PEAR MDB2 library)
40
 * @author Benjamin Eberlei <[email protected]>
41
 * @todo   Rename: PostgreSQLPlatform
42
 */
43
class PostgreSqlPlatform extends AbstractPlatform
44
{
45
    /**
46
     * @var bool
47
     */
48
    private $useBooleanTrueFalseStrings = true;
49
50
    /**
51
     * @var array PostgreSQL booleans literals
52
     */
53
    private $booleanLiterals = array(
54
        'true' => array(
55
            't',
56
            'true',
57
            'y',
58
            'yes',
59
            'on',
60
            '1'
61
        ),
62
        'false' => array(
63
            'f',
64
            'false',
65
            'n',
66
            'no',
67
            'off',
68
            '0'
69
        )
70
    );
71
72
    /**
73
     * PostgreSQL has different behavior with some drivers
74
     * with regard to how booleans have to be handled.
75
     *
76
     * Enables use of 'true'/'false' or otherwise 1 and 0 instead.
77
     *
78
     * @param bool $flag
79
     */
80 8
    public function setUseBooleanTrueFalseStrings($flag)
81
    {
82 8
        $this->useBooleanTrueFalseStrings = (bool) $flag;
83 8
    }
84
85
    /**
86
     * {@inheritDoc}
87
     */
88 4 View Code Duplication
    public function getSubstringExpression($value, $from, $length = 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...
89
    {
90 4
        if ($length === null) {
91 4
            return 'SUBSTRING(' . $value . ' FROM ' . $from . ')';
92
        }
93
94 4
        return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $length . ')';
95
    }
96
97
    /**
98
     * {@inheritDoc}
99
     */
100
    public function getNowExpression()
101
    {
102
        return 'LOCALTIMESTAMP(0)';
103
    }
104
105
    /**
106
     * {@inheritDoc}
107
     */
108 4
    public function getRegexpExpression()
109
    {
110 4
        return 'SIMILAR TO';
111
    }
112
113
    /**
114
     * {@inheritDoc}
115
     */
116
    public function getLocateExpression($str, $substr, $startPos = false)
117
    {
118
        if ($startPos !== false) {
119
            $str = $this->getSubstringExpression($str, $startPos);
120
121
            return 'CASE WHEN (POSITION('.$substr.' IN '.$str.') = 0) THEN 0 ELSE (POSITION('.$substr.' IN '.$str.') + '.($startPos-1).') END';
122
        }
123
124
        return 'POSITION('.$substr.' IN '.$str.')';
125
    }
126
127
    /**
128
     * {@inheritdoc}
129
     */
130
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
131
    {
132
        if (self::DATE_INTERVAL_UNIT_QUARTER === $unit) {
133
            $interval *= 3;
134
            $unit = self::DATE_INTERVAL_UNIT_MONTH;
135
        }
136
137
        return "(" . $date ." " . $operator . " (" . $interval . " || ' " . $unit . "')::interval)";
138
    }
139
140
    /**
141
     * {@inheritDoc}
142
     */
143
    public function getDateDiffExpression($date1, $date2)
144
    {
145
        return '(DATE(' . $date1 . ')-DATE(' . $date2 . '))';
146
    }
147
148
    /**
149
     * {@inheritDoc}
150
     */
151 4
    public function supportsSequences()
152
    {
153 4
        return true;
154
    }
155
156
    /**
157
     * {@inheritDoc}
158
     */
159
    public function supportsSchemas()
160
    {
161
        return true;
162
    }
163
164
    /**
165
     * {@inheritdoc}
166
     */
167
    public function getDefaultSchemaName()
168
    {
169
        return 'public';
170
    }
171
172
    /**
173
     * {@inheritDoc}
174
     */
175 4
    public function supportsIdentityColumns()
176
    {
177 4
        return true;
178
    }
179
180
    /**
181
     * {@inheritdoc}
182
     */
183 34
    public function supportsPartialIndexes()
184
    {
185 34
        return true;
186
    }
187
188
    /**
189
     * {@inheritdoc}
190
     */
191 4
    public function usesSequenceEmulatedIdentityColumns()
192
    {
193 4
        return true;
194
    }
195
196
    /**
197
     * {@inheritdoc}
198
     */
199 4
    public function getIdentitySequenceName($tableName, $columnName)
200
    {
201 4
        return $tableName . '_' . $columnName . '_seq';
202
    }
203
204
    /**
205
     * {@inheritDoc}
206
     */
207 60
    public function supportsCommentOnStatement()
208
    {
209 60
        return true;
210
    }
211
212
    /**
213
     * {@inheritDoc}
214
     */
215 4
    public function prefersSequences()
216
    {
217 4
        return true;
218
    }
219
220
    /**
221
     * {@inheritDoc}
222
     */
223 200
    public function hasNativeGuidType()
224
    {
225 200
        return true;
226
    }
227
228
    /**
229
     * {@inheritDoc}
230
     */
231
    public function getListDatabasesSQL()
232
    {
233
        return 'SELECT datname FROM pg_database';
234
    }
235
236
    /**
237
     * {@inheritDoc}
238
     */
239
    public function getListNamespacesSQL()
240
    {
241
        return "SELECT schema_name AS nspname
242
                FROM   information_schema.schemata
243
                WHERE  schema_name NOT LIKE 'pg\_%'
244
                AND    schema_name != 'information_schema'";
245
    }
246
247
    /**
248
     * {@inheritDoc}
249
     */
250
    public function getListSequencesSQL($database)
251
    {
252
        return "SELECT sequence_name AS relname,
253
                       sequence_schema AS schemaname
254
                FROM   information_schema.sequences
255
                WHERE  sequence_schema NOT LIKE 'pg\_%'
256
                AND    sequence_schema != 'information_schema'";
257
    }
258
259
    /**
260
     * {@inheritDoc}
261
     */
262
    public function getListTablesSQL()
263
    {
264
        return "SELECT quote_ident(table_name) AS table_name,
265
                       table_schema AS schema_name
266
                FROM   information_schema.tables
267
                WHERE  table_schema NOT LIKE 'pg\_%'
268
                AND    table_schema != 'information_schema'
269
                AND    table_name != 'geometry_columns'
270
                AND    table_name != 'spatial_ref_sys'
271
                AND    table_type != 'VIEW'";
272
    }
273
274
    /**
275
     * {@inheritDoc}
276
     */
277
    public function getListViewsSQL($database)
278
    {
279
        return 'SELECT quote_ident(table_name) AS viewname,
280
                       table_schema AS schemaname,
281
                       view_definition AS definition
282
                FROM   information_schema.views
283
                WHERE  view_definition IS NOT NULL';
284
    }
285
286
    /**
287
     * {@inheritDoc}
288
     */
289 8
    public function getListTableForeignKeysSQL($table, $database = null)
0 ignored issues
show
Unused Code introduced by
The parameter $database is not used and could be removed.

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

Loading history...
290
    {
291
        return "SELECT quote_ident(r.conname) as conname, pg_catalog.pg_get_constraintdef(r.oid, true) as condef
292
                  FROM pg_catalog.pg_constraint r
293
                  WHERE r.conrelid =
294
                  (
295
                      SELECT c.oid
296
                      FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n
297 8
                      WHERE " .$this->getTableWhereClause($table) ." AND n.oid = c.relnamespace
298
                  )
299
                  AND r.contype = 'f'";
300
    }
301
302
    /**
303
     * {@inheritDoc}
304
     */
305
    public function getCreateViewSQL($name, $sql)
306
    {
307
        return 'CREATE VIEW ' . $name . ' AS ' . $sql;
308
    }
309
310
    /**
311
     * {@inheritDoc}
312
     */
313
    public function getDropViewSQL($name)
314
    {
315
        return 'DROP VIEW '. $name;
316
    }
317
318
    /**
319
     * {@inheritDoc}
320
     */
321 4
    public function getListTableConstraintsSQL($table)
322
    {
323 4
        $table = new Identifier($table);
324 4
        $table = $this->quoteStringLiteral($table->getName());
325
326
        return "SELECT
327
                    quote_ident(relname) as relname
328
                FROM
329
                    pg_class
330
                WHERE oid IN (
331
                    SELECT indexrelid
332
                    FROM pg_index, pg_class
333 4
                    WHERE pg_class.relname = $table
334
                        AND pg_class.oid = pg_index.indrelid
335
                        AND (indisunique = 't' OR indisprimary = 't')
336
                        )";
337
    }
338
339
    /**
340
     * {@inheritDoc}
341
     *
342
     * @license New BSD License
343
     * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html
344
     */
345 8
    public function getListTableIndexesSQL($table, $currentDatabase = null)
346
    {
347
        return "SELECT quote_ident(relname) as relname, pg_index.indisunique, pg_index.indisprimary,
348
                       pg_index.indkey, pg_index.indrelid,
349
                       pg_get_expr(indpred, indrelid) AS where
350
                 FROM pg_class, pg_index
351
                 WHERE oid IN (
352
                    SELECT indexrelid
353
                    FROM pg_index si, pg_class sc, pg_namespace sn
354 8
                    WHERE " . $this->getTableWhereClause($table, 'sc', 'sn')." AND sc.oid=si.indrelid AND sc.relnamespace = sn.oid
355
                 ) AND pg_index.indexrelid = oid";
356
    }
357
358
    /**
359
     * @param string $table
360
     * @param string $classAlias
361
     * @param string $namespaceAlias
362
     *
363
     * @return string
364
     */
365 24
    private function getTableWhereClause($table, $classAlias = 'c', $namespaceAlias = 'n')
366
    {
367 24
        $whereClause = $namespaceAlias.".nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast') AND ";
368 24 View Code Duplication
        if (strpos($table, ".") !== false) {
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...
369 12
            list($schema, $table) = explode(".", $table);
370 12
            $schema = $this->quoteStringLiteral($schema);
371
        } else {
372 12
            $schema = "ANY(string_to_array((select replace(replace(setting,'\"\$user\"',user),' ','') from pg_catalog.pg_settings where name = 'search_path'),','))";
373
        }
374
375 24
        $table = new Identifier($table);
376 24
        $table = $this->quoteStringLiteral($table->getName());
377 24
        $whereClause .= "$classAlias.relname = " . $table . " AND $namespaceAlias.nspname = $schema";
378
379 24
        return $whereClause;
380
    }
381
382
    /**
383
     * {@inheritDoc}
384
     */
385 8
    public function getListTableColumnsSQL($table, $database = null)
386
    {
387
        return "SELECT
388
                    a.attnum,
389
                    quote_ident(a.attname) AS field,
390
                    t.typname AS type,
391
                    format_type(a.atttypid, a.atttypmod) AS complete_type,
392
                    (SELECT t1.typname FROM pg_catalog.pg_type t1 WHERE t1.oid = t.typbasetype) AS domain_type,
393
                    (SELECT format_type(t2.typbasetype, t2.typtypmod) FROM
394
                      pg_catalog.pg_type t2 WHERE t2.typtype = 'd' AND t2.oid = a.atttypid) AS domain_complete_type,
395
                    a.attnotnull AS isnotnull,
396
                    (SELECT 't'
397
                     FROM pg_index
398
                     WHERE c.oid = pg_index.indrelid
399
                        AND pg_index.indkey[0] = a.attnum
400
                        AND pg_index.indisprimary = 't'
401
                    ) AS pri,
402
                    (SELECT pg_get_expr(adbin, adrelid)
403
                     FROM pg_attrdef
404
                     WHERE c.oid = pg_attrdef.adrelid
405
                        AND pg_attrdef.adnum=a.attnum
406
                    ) AS default,
407
                    (SELECT pg_description.description
408
                        FROM pg_description WHERE pg_description.objoid = c.oid AND a.attnum = pg_description.objsubid
409
                    ) AS comment
410
                    FROM pg_attribute a, pg_class c, pg_type t, pg_namespace n
411 8
                    WHERE ".$this->getTableWhereClause($table, 'c', 'n') ."
412
                        AND a.attnum > 0
413
                        AND a.attrelid = c.oid
414
                        AND a.atttypid = t.oid
415
                        AND n.oid = c.relnamespace
416
                    ORDER BY a.attnum";
417
    }
418
419
    /**
420
     * {@inheritDoc}
421
     */
422 4
    public function getCreateDatabaseSQL($name)
423
    {
424 4
        return 'CREATE DATABASE ' . $name;
425
    }
426
427
    /**
428
     * Returns the SQL statement for disallowing new connections on the given database.
429
     *
430
     * This is useful to force DROP DATABASE operations which could fail because of active connections.
431
     *
432
     * @param string $database The name of the database to disallow new connections for.
433
     *
434
     * @return string
435
     */
436 4
    public function getDisallowDatabaseConnectionsSQL($database)
437
    {
438 4
        return "UPDATE pg_database SET datallowconn = 'false' WHERE datname = '$database'";
439
    }
440
441
    /**
442
     * Returns the SQL statement for closing currently active connections on the given database.
443
     *
444
     * This is useful to force DROP DATABASE operations which could fail because of active connections.
445
     *
446
     * @param string $database The name of the database to close currently active connections for.
447
     *
448
     * @return string
449
     */
450 4
    public function getCloseActiveDatabaseConnectionsSQL($database)
451
    {
452 4
        $database = $this->quoteStringLiteral($database);
453
454 4
        return "SELECT pg_terminate_backend(procpid) FROM pg_stat_activity WHERE datname = $database";
455
    }
456
457
    /**
458
     * {@inheritDoc}
459
     */
460 20
    public function getAdvancedForeignKeyOptionsSQL(\Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey)
461
    {
462 20
        $query = '';
463
464 20
        if ($foreignKey->hasOption('match')) {
465 4
            $query .= ' MATCH ' . $foreignKey->getOption('match');
466
        }
467
468 20
        $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
469
470 20
        if ($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false) {
471 4
            $query .= ' DEFERRABLE';
472
        } else {
473 20
            $query .= ' NOT DEFERRABLE';
474
        }
475
476 20
        if (($foreignKey->hasOption('feferred') && $foreignKey->getOption('feferred') !== false)
477 20
            || ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false)
478
        ) {
479 4
            $query .= ' INITIALLY DEFERRED';
480
        } else {
481 20
            $query .= ' INITIALLY IMMEDIATE';
482
        }
483
484 20
        return $query;
485
    }
486
487
    /**
488
     * {@inheritDoc}
489
     */
490 76
    public function getAlterTableSQL(TableDiff $diff)
491
    {
492 76
        $sql = array();
493 76
        $commentsSQL = array();
494 76
        $columnSql = array();
495
496 76
        foreach ($diff->addedColumns as $column) {
497 16
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
498
                continue;
499
            }
500
501 16
            $query = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
502 16
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
503
504 16
            $comment = $this->getColumnComment($column);
505
506 16 View Code Duplication
            if (null !== $comment && '' !== $comment) {
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...
507 4
                $commentsSQL[] = $this->getCommentOnColumnSQL(
508 4
                    $diff->getName($this)->getQuotedName($this),
509 4
                    $column->getQuotedName($this),
510 16
                    $comment
511
                );
512
            }
513
        }
514
515 76
        foreach ($diff->removedColumns as $column) {
516 16
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
517
                continue;
518
            }
519
520 16
            $query = 'DROP ' . $column->getQuotedName($this);
521 16
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
522
        }
523
524 76
        foreach ($diff->changedColumns as $columnDiff) {
525
            /** @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */
526 44
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
527
                continue;
528
            }
529
530 44
            if ($this->isUnchangedBinaryColumn($columnDiff)) {
531 4
                continue;
532
            }
533
534 40
            $oldColumnName = $columnDiff->getOldColumnName()->getQuotedName($this);
535 40
            $column = $columnDiff->column;
536
537 40
            if ($columnDiff->hasChanged('type') || $columnDiff->hasChanged('precision') || $columnDiff->hasChanged('scale') || $columnDiff->hasChanged('fixed')) {
538 20
                $type = $column->getType();
539
540
                // SERIAL/BIGSERIAL are not "real" types and we can't alter a column to that type
541 20
                $columnDefinition = $column->toArray();
542 20
                $columnDefinition['autoincrement'] = false;
543
544
                // here was a server version check before, but DBAL API does not support this anymore.
545 20
                $query = 'ALTER ' . $oldColumnName . ' TYPE ' . $type->getSQLDeclaration($columnDefinition, $this);
546 20
                $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
547
            }
548
549 40
            if ($columnDiff->hasChanged('default') || $this->typeChangeBreaksDefaultValue($columnDiff)) {
550 12
                $defaultClause = null === $column->getDefault()
551 8
                    ? ' DROP DEFAULT'
552 12
                    : ' SET' . $this->getDefaultValueDeclarationSQL($column->toArray());
553 12
                $query = 'ALTER ' . $oldColumnName . $defaultClause;
554 12
                $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
555
            }
556
557 40
            if ($columnDiff->hasChanged('notnull')) {
558 8
                $query = 'ALTER ' . $oldColumnName . ' ' . ($column->getNotnull() ? 'SET' : 'DROP') . ' NOT NULL';
559 8
                $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
560
            }
561
562 40
            if ($columnDiff->hasChanged('autoincrement')) {
563
                if ($column->getAutoincrement()) {
564
                    // add autoincrement
565
                    $seqName = $this->getIdentitySequenceName($diff->name, $oldColumnName);
566
567
                    $sql[] = "CREATE SEQUENCE " . $seqName;
568
                    $sql[] = "SELECT setval('" . $seqName . "', (SELECT MAX(" . $oldColumnName . ") FROM " . $diff->getName($this)->getQuotedName($this) . "))";
569
                    $query = "ALTER " . $oldColumnName . " SET DEFAULT nextval('" . $seqName . "')";
570
                    $sql[] = "ALTER TABLE " . $diff->getName($this)->getQuotedName($this) . " " . $query;
571
                } else {
572
                    // Drop autoincrement, but do NOT drop the sequence. It might be re-used by other tables or have
573
                    $query = "ALTER " . $oldColumnName . " " . "DROP DEFAULT";
574
                    $sql[] = "ALTER TABLE " . $diff->getName($this)->getQuotedName($this) . " " . $query;
575
                }
576
            }
577
578 40
            $comment = $this->getColumnComment($column);
579
580 40 View Code Duplication
            if (null !== $comment && '' !== $comment) {
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...
581 16
                $commentsSQL[] = $this->getCommentOnColumnSQL(
582 16
                    $diff->getName($this)->getQuotedName($this),
583 16
                    $column->getQuotedName($this),
584 16
                    $comment
585
                );
586
            }
587
588 40
            if ($columnDiff->hasChanged('length')) {
589 4
                $query = 'ALTER ' . $oldColumnName . ' TYPE ' . $column->getType()->getSQLDeclaration($column->toArray(), $this);
590 40
                $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
591
            }
592
        }
593
594 76 View Code Duplication
        foreach ($diff->renamedColumns as $oldColumnName => $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...
595 16
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
596
                continue;
597
            }
598
599 16
            $oldColumnName = new Identifier($oldColumnName);
600
601 16
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) .
602 16
                ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this);
603
        }
604
605 76
        $tableSql = array();
606
607 76 View Code Duplication
        if ( ! $this->onSchemaAlterTable($diff, $tableSql)) {
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...
608 76
            $sql = array_merge($sql, $commentsSQL);
609
610 76
            if ($diff->newName !== false) {
611 8
                $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' RENAME TO ' . $diff->getNewName()->getQuotedName($this);
612
            }
613
614 76
            $sql = array_merge(
615 76
                $this->getPreAlterTableIndexForeignKeySQL($diff),
616 76
                $sql,
617 76
                $this->getPostAlterTableIndexForeignKeySQL($diff)
618
            );
619
        }
620
621 76
        return array_merge($sql, $tableSql, $columnSql);
622
    }
623
624
    /**
625
     * Checks whether a given column diff is a logically unchanged binary type column.
626
     *
627
     * Used to determine whether a column alteration for a binary type column can be skipped.
628
     * Doctrine's {@link \Doctrine\DBAL\Types\BinaryType} and {@link \Doctrine\DBAL\Types\BlobType}
629
     * are mapped to the same database column type on this platform as this platform
630
     * does not have a native VARBINARY/BINARY column type. Therefore the {@link \Doctrine\DBAL\Schema\Comparator}
631
     * might detect differences for binary type columns which do not have to be propagated
632
     * to database as there actually is no difference at database level.
633
     *
634
     * @param ColumnDiff $columnDiff The column diff to check against.
635
     *
636
     * @return boolean True if the given column diff is an unchanged binary type column, false otherwise.
637
     */
638 44
    private function isUnchangedBinaryColumn(ColumnDiff $columnDiff)
639
    {
640 44
        $columnType = $columnDiff->column->getType();
641
642 44
        if ( ! $columnType instanceof BinaryType && ! $columnType instanceof BlobType) {
643 40
            return false;
644
        }
645
646 4
        $fromColumn = $columnDiff->fromColumn instanceof Column ? $columnDiff->fromColumn : null;
647
648 4
        if ($fromColumn) {
649 4
            $fromColumnType = $fromColumn->getType();
650
651 4
            if ( ! $fromColumnType instanceof BinaryType && ! $fromColumnType instanceof BlobType) {
652
                return false;
653
            }
654
655 4
            return count(array_diff($columnDiff->changedProperties, array('type', 'length', 'fixed'))) === 0;
656
        }
657
658
        if ($columnDiff->hasChanged('type')) {
659
            return false;
660
        }
661
662
        return count(array_diff($columnDiff->changedProperties, array('length', 'fixed'))) === 0;
663
    }
664
665
    /**
666
     * {@inheritdoc}
667
     */
668 20 View Code Duplication
    protected function getRenameIndexSQL($oldIndexName, Index $index, $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...
669
    {
670 20
        if (strpos($tableName, '.') !== false) {
671 8
            list($schema) = explode('.', $tableName);
672 8
            $oldIndexName = $schema . '.' . $oldIndexName;
673
        }
674
675 20
        return array('ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this));
676
    }
677
678
    /**
679
     * {@inheritdoc}
680
     */
681 40 View Code Duplication
    public function getCommentOnColumnSQL($tableName, $columnName, $comment)
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...
682
    {
683 40
        $tableName = new Identifier($tableName);
684 40
        $columnName = new Identifier($columnName);
685 40
        $comment = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment);
686
687 40
        return "COMMENT ON COLUMN " . $tableName->getQuotedName($this) . "." . $columnName->getQuotedName($this) .
688 40
            " IS $comment";
689
    }
690
691
    /**
692
     * {@inheritDoc}
693
     */
694 8 View Code Duplication
    public function getCreateSequenceSQL(Sequence $sequence)
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...
695
    {
696 8
        return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) .
697 8
               ' INCREMENT BY ' . $sequence->getAllocationSize() .
698 8
               ' MINVALUE ' . $sequence->getInitialValue() .
699 8
               ' START ' . $sequence->getInitialValue() .
700 8
               $this->getSequenceCacheSQL($sequence);
701
    }
702
703
    /**
704
     * {@inheritDoc}
705
     */
706
    public function getAlterSequenceSQL(Sequence $sequence)
707
    {
708
        return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) .
709
               ' INCREMENT BY ' . $sequence->getAllocationSize() .
710
               $this->getSequenceCacheSQL($sequence);
711
    }
712
713
    /**
714
     * Cache definition for sequences
715
     *
716
     * @param Sequence $sequence
717
     *
718
     * @return string
719
     */
720 8
    private function getSequenceCacheSQL(Sequence $sequence)
721
    {
722 8
        if ($sequence->getCache() > 1) {
723 4
            return ' CACHE ' . $sequence->getCache();
724
        }
725
726 4
        return '';
727
    }
728
729
    /**
730
     * {@inheritDoc}
731
     */
732 4
    public function getDropSequenceSQL($sequence)
733
    {
734 4
        if ($sequence instanceof Sequence) {
735
            $sequence = $sequence->getQuotedName($this);
736
        }
737
738 4
        return 'DROP SEQUENCE ' . $sequence . ' CASCADE';
739
    }
740
741
    /**
742
     * {@inheritDoc}
743
     */
744 4
    public function getCreateSchemaSQL($schemaName)
745
    {
746 4
        return 'CREATE SCHEMA ' . $schemaName;
747
    }
748
749
    /**
750
     * {@inheritDoc}
751
     */
752 12
    public function getDropForeignKeySQL($foreignKey, $table)
753
    {
754 12
        return $this->getDropConstraintSQL($foreignKey, $table);
755
    }
756
757
    /**
758
     * {@inheritDoc}
759
     */
760 56
    protected function _getCreateTableSQL($tableName, array $columns, array $options = array())
761
    {
762 56
        $queryFields = $this->getColumnDeclarationListSQL($columns);
763
764 56 View Code Duplication
        if (isset($options['primary']) && ! empty($options['primary'])) {
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...
765 16
            $keyColumns = array_unique(array_values($options['primary']));
766 16
            $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
767
        }
768
769 56
        $query = 'CREATE TABLE ' . $tableName . ' (' . $queryFields . ')';
770
771 56
        $sql[] = $query;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$sql was never initialized. Although not strictly required by PHP, it is generally a good practice to add $sql = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
772
773 56 View Code Duplication
        if (isset($options['indexes']) && ! empty($options['indexes'])) {
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...
774 12
            foreach ($options['indexes'] as $index) {
775 12
                $sql[] = $this->getCreateIndexSQL($index, $tableName);
776
            }
777
        }
778
779 56 View Code Duplication
        if (isset($options['foreignKeys'])) {
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...
780 4
            foreach ((array) $options['foreignKeys'] as $definition) {
781 4
                $sql[] = $this->getCreateForeignKeySQL($definition, $tableName);
782
            }
783
        }
784
785 56
        return $sql;
786
    }
787
788
    /**
789
     * Converts a single boolean value.
790
     *
791
     * First converts the value to its native PHP boolean type
792
     * and passes it to the given callback function to be reconverted
793
     * into any custom representation.
794
     *
795
     * @param mixed    $value    The value to convert.
796
     * @param callable $callback The callback function to use for converting the real boolean value.
797
     *
798
     * @return mixed
799
     * @throws \UnexpectedValueException
800
     */
801 128
    private function convertSingleBooleanValue($value, $callback)
802
    {
803 128
        if (null === $value) {
804 8
            return $callback(null);
805
        }
806
807 120
        if (is_bool($value) || is_numeric($value)) {
808 76
            return $callback($value ? true : false);
809
        }
810
811 44
        if (!is_string($value)) {
812
            return $callback(true);
813
        }
814
815
        /**
816
         * Better safe than sorry: http://php.net/in_array#106319
817
         */
818 44 View Code Duplication
        if (in_array(trim(strtolower($value)), $this->booleanLiterals['false'], 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...
819 20
            return $callback(false);
820
        }
821
822 24 View Code Duplication
        if (in_array(trim(strtolower($value)), $this->booleanLiterals['true'], 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...
823 20
            return $callback(true);
824
        }
825
826 4
        throw new \UnexpectedValueException("Unrecognized boolean literal '${value}'");
827
    }
828
829
    /**
830
     * Converts one or multiple boolean values.
831
     *
832
     * First converts the value(s) to their native PHP boolean type
833
     * and passes them to the given callback function to be reconverted
834
     * into any custom representation.
835
     *
836
     * @param mixed    $item     The value(s) to convert.
837
     * @param callable $callback The callback function to use for converting the real boolean value(s).
838
     *
839
     * @return mixed
840
     */
841
    private function doConvertBooleans($item, $callback)
842
    {
843 128
        if (is_array($item)) {
844
            foreach ($item as $key => $value) {
845
                $item[$key] = $this->convertSingleBooleanValue($value, $callback);
846
            }
847
848
            return $item;
849
        }
850
851 128
        return $this->convertSingleBooleanValue($item, $callback);
852
    }
853
854
    /**
855
     * {@inheritDoc}
856
     *
857
     * Postgres wants boolean values converted to the strings 'true'/'false'.
858
     */
859
    public function convertBooleans($item)
860
    {
861 72
        if ( ! $this->useBooleanTrueFalseStrings) {
862 8
            return parent::convertBooleans($item);
863
        }
864
865 64
        return $this->doConvertBooleans(
866 64
            $item,
867
            function ($boolean) {
868 64
                if (null === $boolean) {
869 4
                    return 'NULL';
870
                }
871
872 60
                return true === $boolean ? 'true' : 'false';
873 64
            }
874
        );
875
    }
876
877
    /**
878
     * {@inheritDoc}
879
     */
880
    public function convertBooleansToDatabaseValue($item)
881
    {
882 68
        if ( ! $this->useBooleanTrueFalseStrings) {
883 4
            return parent::convertBooleansToDatabaseValue($item);
884
        }
885
886 64
        return $this->doConvertBooleans(
887 64
            $item,
888
            function ($boolean) {
889 60
                return null === $boolean ? null : (int) $boolean;
890 64
            }
891
        );
892
    }
893
894
    /**
895
     * {@inheritDoc}
896
     */
897
    public function convertFromBoolean($item)
898
    {
899 60
        if (in_array(strtolower($item), $this->booleanLiterals['false'], true)) {
900 24
            return false;
901
        }
902
903 36
        return parent::convertFromBoolean($item);
904
    }
905
906
    /**
907
     * {@inheritDoc}
908
     */
909
    public function getSequenceNextValSQL($sequenceName)
910
    {
911 4
        return "SELECT NEXTVAL('" . $sequenceName . "')";
912
    }
913
914
    /**
915
     * {@inheritDoc}
916
     */
917
    public function getSetTransactionIsolationSQL($level)
918
    {
919
        return 'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL '
920 4
                . $this->_getTransactionIsolationLevelSQL($level);
921
    }
922
923
    /**
924
     * {@inheritDoc}
925
     */
926
    public function getBooleanTypeDeclarationSQL(array $field)
927
    {
928 4
        return 'BOOLEAN';
929
    }
930
931
    /**
932
     * {@inheritDoc}
933
     */
934
    public function getIntegerTypeDeclarationSQL(array $field)
935
    {
936 44
        if ( ! empty($field['autoincrement'])) {
937 20
            return 'SERIAL';
938
        }
939
940 28
        return 'INT';
941
    }
942
943
    /**
944
     * {@inheritDoc}
945
     */
946
    public function getBigIntTypeDeclarationSQL(array $field)
947
    {
948 8
        if ( ! empty($field['autoincrement'])) {
949 8
            return 'BIGSERIAL';
950
        }
951
952
        return 'BIGINT';
953
    }
954
955
    /**
956
     * {@inheritDoc}
957
     */
958
    public function getSmallIntTypeDeclarationSQL(array $field)
959
    {
960 2
        return 'SMALLINT';
961
    }
962
963
    /**
964
     * {@inheritDoc}
965
     */
966
    public function getGuidTypeDeclarationSQL(array $field)
967
    {
968 4
        return 'UUID';
969
    }
970
971
    /**
972
     * {@inheritDoc}
973
     */
974
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
975
    {
976 4
        return 'TIMESTAMP(0) WITHOUT TIME ZONE';
977
    }
978
979
    /**
980
     * {@inheritDoc}
981
     */
982
    public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration)
983
    {
984
        return 'TIMESTAMP(0) WITH TIME ZONE';
985
    }
986
987
    /**
988
     * {@inheritDoc}
989
     */
990
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
991
    {
992
        return 'DATE';
993
    }
994
995
    /**
996
     * {@inheritDoc}
997
     */
998
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
999
    {
1000
        return 'TIME(0) WITHOUT TIME ZONE';
1001
    }
1002
1003
    /**
1004
     * {@inheritDoc}
1005
     */
1006
    public function getGuidExpression()
1007
    {
1008
        return 'UUID_GENERATE_V4()';
1009
    }
1010
1011
    /**
1012
     * {@inheritDoc}
1013
     */
1014
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
1015
    {
1016
        return '';
1017
    }
1018
1019
    /**
1020
     * {@inheritDoc}
1021
     */
1022
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
1023
    {
1024 48
        return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
1025 48
                : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)');
1026
    }
1027
1028
    /**
1029
     * {@inheritdoc}
1030
     */
1031
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
1032
    {
1033 4
        return 'BYTEA';
1034
    }
1035
1036
    /**
1037
     * {@inheritDoc}
1038
     */
1039
    public function getClobTypeDeclarationSQL(array $field)
1040
    {
1041 6
        return 'TEXT';
1042
    }
1043
1044
    /**
1045
     * {@inheritDoc}
1046
     */
1047
    public function getName()
1048
    {
1049 8
        return 'postgresql';
1050
    }
1051
1052
    /**
1053
     * {@inheritDoc}
1054
     *
1055
     * PostgreSQL returns all column names in SQL result sets in lowercase.
1056
     */
1057
    public function getSQLResultCasing($column)
1058
    {
1059
        return strtolower($column);
1060
    }
1061
1062
    /**
1063
     * {@inheritDoc}
1064
     */
1065
    public function getDateTimeTzFormatString()
1066
    {
1067
        return 'Y-m-d H:i:sO';
1068
    }
1069
1070
    /**
1071
     * {@inheritDoc}
1072
     */
1073
    public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName)
1074
    {
1075
        return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (DEFAULT)';
1076
    }
1077
1078
    /**
1079
     * {@inheritDoc}
1080
     */
1081
    public function getTruncateTableSQL($tableName, $cascade = false)
1082
    {
1083 4
        $tableIdentifier = new Identifier($tableName);
1084 4
        $sql = 'TRUNCATE ' . $tableIdentifier->getQuotedName($this);
1085
1086 4
        if ($cascade) {
1087
            $sql .= ' CASCADE';
1088
        }
1089
1090 4
        return $sql;
1091
    }
1092
1093
    /**
1094
     * {@inheritDoc}
1095
     */
1096
    public function getReadLockSQL()
1097
    {
1098
        return 'FOR SHARE';
1099
    }
1100
1101
    /**
1102
     * {@inheritDoc}
1103
     */
1104 View Code Duplication
    protected function initializeDoctrineTypeMappings()
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...
1105
    {
1106 22
        $this->doctrineTypeMapping = array(
1107
            'smallint'      => 'smallint',
1108
            'int2'          => 'smallint',
1109
            'serial'        => 'integer',
1110
            'serial4'       => 'integer',
1111
            'int'           => 'integer',
1112
            'int4'          => 'integer',
1113
            'integer'       => 'integer',
1114
            'bigserial'     => 'bigint',
1115
            'serial8'       => 'bigint',
1116
            'bigint'        => 'bigint',
1117
            'int8'          => 'bigint',
1118
            'bool'          => 'boolean',
1119
            'boolean'       => 'boolean',
1120
            'text'          => 'text',
1121
            'tsvector'      => 'text',
1122
            'varchar'       => 'string',
1123
            'interval'      => 'string',
1124
            '_varchar'      => 'string',
1125
            'char'          => 'string',
1126
            'bpchar'        => 'string',
1127
            'inet'          => 'string',
1128
            'date'          => 'date',
1129
            'datetime'      => 'datetime',
1130
            'timestamp'     => 'datetime',
1131
            'timestamptz'   => 'datetimetz',
1132
            'time'          => 'time',
1133
            'timetz'        => 'time',
1134
            'float'         => 'float',
1135
            'float4'        => 'float',
1136
            'float8'        => 'float',
1137
            'double'        => 'float',
1138
            'double precision' => 'float',
1139
            'real'          => 'float',
1140
            'decimal'       => 'decimal',
1141
            'money'         => 'decimal',
1142
            'numeric'       => 'decimal',
1143
            'year'          => 'date',
1144
            'uuid'          => 'guid',
1145
            'bytea'         => 'blob',
1146
        );
1147 22
    }
1148
1149
    /**
1150
     * {@inheritDoc}
1151
     */
1152
    public function getVarcharMaxLength()
1153
    {
1154 48
        return 65535;
1155
    }
1156
1157
    /**
1158
     * {@inheritdoc}
1159
     */
1160
    public function getBinaryMaxLength()
1161
    {
1162 8
        return 0;
1163
    }
1164
1165
    /**
1166
     * {@inheritdoc}
1167
     */
1168
    public function getBinaryDefaultLength()
1169
    {
1170 8
        return 0;
1171
    }
1172
1173
    /**
1174
     * {@inheritDoc}
1175
     */
1176
    protected function getReservedKeywordsClass()
1177
    {
1178 52
        return Keywords\PostgreSQLKeywords::class;
1179
    }
1180
1181
    /**
1182
     * {@inheritDoc}
1183
     */
1184
    public function getBlobTypeDeclarationSQL(array $field)
1185
    {
1186 4
        return 'BYTEA';
1187
    }
1188
1189
    /**
1190
     * {@inheritdoc}
1191
     */
1192
    public function quoteStringLiteral($str)
1193
    {
1194 76
        $str = str_replace('\\', '\\\\', $str); // PostgreSQL requires backslashes to be escaped aswell.
1195
1196 76
        return parent::quoteStringLiteral($str);
1197
    }
1198
1199
    /**
1200
     * {@inheritdoc}
1201
     */
1202
    public function getDefaultValueDeclarationSQL($field)
1203
    {
1204 96
        if ($this->isSerialField($field)) {
1205 32
            return '';
1206
        }
1207
1208 68
        return parent::getDefaultValueDeclarationSQL($field);
1209
    }
1210
1211
    private function isSerialField(array $field) : bool
1212
    {
1213 96
        return $field['autoincrement'] ?? false === true && isset($field['type'])
1214
            && $this->isNumericType($field['type']);
1215
    }
1216
1217
    /**
1218
     * Check whether the type of a column is changed in a way that invalidates the default value for the column
1219
     *
1220
     * @param ColumnDiff $columnDiff
1221
     * @return bool
1222
     */
1223
    private function typeChangeBreaksDefaultValue(ColumnDiff $columnDiff) : bool
1224
    {
1225 36
        if (! $columnDiff->fromColumn) {
1226 20
            return $columnDiff->hasChanged('type');
1227
        }
1228
1229 16
        $oldTypeIsNumeric = $this->isNumericType($columnDiff->fromColumn->getType());
1230 16
        $newTypeIsNumeric = $this->isNumericType($columnDiff->column->getType());
1231
1232
        // default should not be changed when switching between numeric types and the default comes from a sequence
1233 16
        return $columnDiff->hasChanged('type')
1234 16
            && ! ($oldTypeIsNumeric && $newTypeIsNumeric && $columnDiff->column->getAutoincrement());
1235
    }
1236
1237
    private function isNumericType(Type $type) : bool
1238
    {
1239 16
        return $type instanceof IntegerType || $type instanceof BigIntType;
1240
    }
1241
}
1242