Completed
Push — develop ( 0c4aa7...b62acb )
by Sergei
55s queued 14s
created

PostgreSqlPlatform   F

Complexity

Total Complexity 163

Size/Duplication

Total Lines 1209
Duplicated Lines 0 %

Test Coverage

Coverage 92.29%

Importance

Changes 0
Metric Value
wmc 163
eloc 359
dl 0
loc 1209
ccs 347
cts 376
cp 0.9229
rs 2
c 0
b 0
f 0

82 Methods

Rating   Name   Duplication   Size   Complexity  
A getRenameIndexSQL() 0 8 2
A getDateDiffExpression() 0 3 1
A getListTableForeignKeysSQL() 0 9 1
A getDateArithmeticIntervalExpression() 0 8 2
A getTableWhereClause() 0 19 2
A getLocateExpression() 0 9 2
A supportsPartialIndexes() 0 3 1
A getIdentitySequenceName() 0 3 1
A getListSequencesSQL() 0 3 1
A supportsSequences() 0 3 1
A getRegexpExpression() 0 3 1
A getDefaultSchemaName() 0 3 1
A supportsSchemas() 0 3 1
A getDisallowDatabaseConnectionsSQL() 0 3 1
A getListTablesSQL() 0 3 1
A getListTableColumnsSQL() 0 28 1
A getCreateViewSQL() 0 3 1
A usesSequenceEmulatedIdentityColumns() 0 3 1
A getListTableIndexesSQL() 0 10 1
A getListNamespacesSQL() 0 3 1
A getNowExpression() 0 3 1
A getListTableConstraintsSQL() 0 21 1
A getDropViewSQL() 0 3 1
A getListViewsSQL() 0 3 1
A getCreateDatabaseSQL() 0 3 1
A supportsIdentityColumns() 0 3 1
A supportsCommentOnStatement() 0 3 1
A setUseBooleanTrueFalseStrings() 0 3 1
A getListDatabasesSQL() 0 3 1
A hasNativeGuidType() 0 3 1
A prefersSequences() 0 3 1
A getCloseActiveDatabaseConnectionsSQL() 0 4 1
A getReadLockSQL() 0 3 1
A getName() 0 3 1
A getDateTimeTzFormatString() 0 3 1
A getEmptyIdentityInsertSQL() 0 3 1
A getSQLResultCasing() 0 3 1
A initializeDoctrineTypeMappings() 0 43 1
A getClobTypeDeclarationSQL() 0 3 1
A getTruncateTableSQL() 0 10 2
A getCreateSequenceSQL() 0 7 1
A convertFromBoolean() 0 7 2
A getAlterSequenceSQL() 0 5 1
A getBigIntTypeDeclarationSQL() 0 7 2
A getIntegerTypeDeclarationSQL() 0 7 2
A getCommentOnColumnSQL() 0 11 2
A convertBooleansToDatabaseValue() 0 10 3
B isUnchangedBinaryColumn() 0 25 7
A _getCommonIntegerTypeDeclarationSQL() 0 3 1
A getGuidTypeDeclarationSQL() 0 3 1
A getDropForeignKeySQL() 0 3 1
A getTimeTypeDeclarationSQL() 0 3 1
A getCreateSchemaSQL() 0 3 1
A getDropSequenceSQL() 0 7 2
A doConvertBooleans() 0 11 3
A getDateTimeTzTypeDeclarationSQL() 0 3 1
A getSmallIntTypeDeclarationSQL() 0 7 2
A getDateTypeDeclarationSQL() 0 3 1
B _getCreateTableSQL() 0 26 8
A getSequenceNextValSQL() 0 3 1
A getSetTransactionIsolationSQL() 0 4 1
F getAlterTableSQL() 0 143 29
A getSequenceCacheSQL() 0 7 2
A getBooleanTypeDeclarationSQL() 0 3 1
A getAdvancedForeignKeyOptionsSQL() 0 23 6
A getDateTimeTypeDeclarationSQL() 0 3 1
B convertSingleBooleanValue() 0 28 7
A convertBooleans() 0 14 4
A getBinaryTypeDeclarationSQLSnippet() 0 3 1
A getVarcharTypeDeclarationSQLSnippet() 0 9 2
A hasNativeJsonType() 0 3 1
A getJsonTypeDeclarationSQL() 0 3 1
A isNumericType() 0 3 2
A typeChangeBreaksDefaultValue() 0 12 5
A getReservedKeywordsClass() 0 3 1
A getVarbinaryTypeDeclarationSQLSnippet() 0 3 1
A getBlobTypeDeclarationSQL() 0 3 1
A supportsColumnCollation() 0 3 1
A getDefaultValueDeclarationSQL() 0 7 2
A getOldColumnComment() 0 7 2
A isSerialField() 0 5 3
A getColumnCollationDeclarationSQL() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like PostgreSqlPlatform often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PostgreSqlPlatform, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\DBAL\Platforms;
6
7
use Doctrine\DBAL\Schema\ColumnDiff;
8
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
9
use Doctrine\DBAL\Schema\Identifier;
10
use Doctrine\DBAL\Schema\Index;
11
use Doctrine\DBAL\Schema\Sequence;
12
use Doctrine\DBAL\Schema\TableDiff;
13
use Doctrine\DBAL\Types\BigIntType;
14
use Doctrine\DBAL\Types\BinaryType;
15
use Doctrine\DBAL\Types\BlobType;
16
use Doctrine\DBAL\Types\IntegerType;
17
use Doctrine\DBAL\Types\Type;
18
use UnexpectedValueException;
19
use function array_diff;
20
use function array_merge;
21
use function array_unique;
22
use function array_values;
23
use function count;
24
use function explode;
25
use function implode;
26
use function in_array;
27
use function is_array;
28
use function is_bool;
29
use function is_numeric;
30
use function is_string;
31
use function sprintf;
32
use function strpos;
33
use function strtolower;
34
use function trim;
35
36
/**
37
 * Provides the behavior, features and SQL dialect of the PostgreSQL 9.4+ database platform.
38
 *
39
 * @todo   Rename: PostgreSQLPlatform
40
 */
41
class PostgreSqlPlatform extends AbstractPlatform
42
{
43
    /** @var bool */
44
    private $useBooleanTrueFalseStrings = true;
45
46
    /** @var string[][] PostgreSQL booleans literals */
47
    private $booleanLiterals = [
48
        'true' => [
49
            't',
50
            'true',
51
            'y',
52
            'yes',
53
            'on',
54
            '1',
55
        ],
56
        'false' => [
57
            'f',
58
            'false',
59
            'n',
60
            'no',
61
            'off',
62
            '0',
63
        ],
64
    ];
65
66
    /**
67
     * PostgreSQL has different behavior with some drivers
68
     * with regard to how booleans have to be handled.
69
     *
70
     * Enables use of 'true'/'false' or otherwise 1 and 0 instead.
71
     */
72 6981
    public function setUseBooleanTrueFalseStrings(bool $flag) : void
73
    {
74 6981
        $this->useBooleanTrueFalseStrings = $flag;
75 6981
    }
76
77
    /**
78
     * {@inheritDoc}
79
     */
80
    public function getNowExpression() : string
81
    {
82
        return 'LOCALTIMESTAMP(0)';
83
    }
84
85
    /**
86
     * {@inheritDoc}
87
     */
88 7378
    public function getRegexpExpression() : string
89
    {
90 7378
        return 'SIMILAR TO';
91
    }
92
93
    /**
94
     * {@inheritDoc}
95
     */
96 2944
    public function getLocateExpression(string $string, string $substring, ?string $start = null) : string
97
    {
98 2944
        if ($start !== null) {
99 2944
            $string = $this->getSubstringExpression($string, $start);
100
101 2944
            return 'CASE WHEN (POSITION(' . $substring . ' IN ' . $string . ') = 0) THEN 0 ELSE (POSITION(' . $substring . ' IN ' . $string . ') + ' . $start . ' - 1) END';
102
        }
103
104 2944
        return sprintf('POSITION(%s IN %s)', $substring, $string);
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     */
110 3136
    protected function getDateArithmeticIntervalExpression(string $date, string $operator, string $interval, string $unit) : string
111
    {
112 3136
        if ($unit === DateIntervalUnit::QUARTER) {
113 2992
            $interval = $this->multiplyInterval($interval, 3);
114 2992
            $unit     = DateIntervalUnit::MONTH;
115
        }
116
117 3136
        return '(' . $date . ' ' . $operator . ' (' . $interval . " || ' " . $unit . "')::interval)";
118
    }
119
120
    /**
121
     * {@inheritDoc}
122
     */
123 2770
    public function getDateDiffExpression(string $date1, string $date2) : string
124
    {
125 2770
        return '(DATE(' . $date1 . ')-DATE(' . $date2 . '))';
126
    }
127
128
    /**
129
     * {@inheritDoc}
130
     */
131 2474
    public function supportsSequences() : bool
132
    {
133 2474
        return true;
134
    }
135
136
    /**
137
     * {@inheritDoc}
138
     */
139 2453
    public function supportsSchemas() : bool
140
    {
141 2453
        return true;
142
    }
143
144
    /**
145
     * {@inheritdoc}
146
     */
147 2303
    public function getDefaultSchemaName() : string
148
    {
149 2303
        return 'public';
150
    }
151
152
    /**
153
     * {@inheritDoc}
154
     */
155 2372
    public function supportsIdentityColumns() : bool
156
    {
157 2372
        return true;
158
    }
159
160
    /**
161
     * {@inheritdoc}
162
     */
163 8669
    public function supportsPartialIndexes() : bool
164
    {
165 8669
        return true;
166
    }
167
168
    /**
169
     * {@inheritdoc}
170
     */
171 6963
    public function usesSequenceEmulatedIdentityColumns() : bool
172
    {
173 6963
        return true;
174
    }
175
176
    /**
177
     * {@inheritdoc}
178
     */
179 7710
    public function getIdentitySequenceName(string $tableName, string $columnName) : string
180
    {
181 7710
        return $tableName . '_' . $columnName . '_seq';
182
    }
183
184
    /**
185
     * {@inheritDoc}
186
     */
187 8783
    public function supportsCommentOnStatement() : bool
188
    {
189 8783
        return true;
190
    }
191
192
    /**
193
     * {@inheritDoc}
194
     */
195 3
    public function prefersSequences() : bool
196
    {
197 3
        return true;
198
    }
199
200
    /**
201
     * {@inheritDoc}
202
     */
203 8885
    public function hasNativeGuidType() : bool
204
    {
205 8885
        return true;
206
    }
207
208
    /**
209
     * {@inheritDoc}
210
     */
211 2477
    public function getListDatabasesSQL() : string
212
    {
213 2477
        return 'SELECT datname FROM pg_database';
214
    }
215
216
    /**
217
     * {@inheritDoc}
218
     */
219 2453
    public function getListNamespacesSQL() : string
220
    {
221 2453
        return "SELECT schema_name AS nspname
222
                FROM   information_schema.schemata
223
                WHERE  schema_name NOT LIKE 'pg\_%'
224
                AND    schema_name != 'information_schema'";
225
    }
226
227
    /**
228
     * {@inheritDoc}
229
     */
230 1648
    public function getListSequencesSQL(string $database) : string
231
    {
232 1648
        return "SELECT sequence_name AS relname,
233
                       sequence_schema AS schemaname
234
                FROM   information_schema.sequences
235
                WHERE  sequence_schema NOT LIKE 'pg\_%'
236
                AND    sequence_schema != 'information_schema'";
237
    }
238
239
    /**
240
     * {@inheritDoc}
241
     */
242 2818
    public function getListTablesSQL() : string
243
    {
244 2818
        return "SELECT quote_ident(table_name) AS table_name,
245
                       table_schema AS schema_name
246
                FROM   information_schema.tables
247
                WHERE  table_schema NOT LIKE 'pg\_%'
248
                AND    table_schema != 'information_schema'
249
                AND    table_name != 'geometry_columns'
250
                AND    table_name != 'spatial_ref_sys'
251
                AND    table_type != 'VIEW'";
252
    }
253
254
    /**
255
     * {@inheritDoc}
256
     */
257 2375
    public function getListViewsSQL(string $database) : string
258
    {
259 2375
        return 'SELECT quote_ident(table_name) AS viewname,
260
                       table_schema AS schemaname,
261
                       view_definition AS definition
262
                FROM   information_schema.views
263
                WHERE  view_definition IS NOT NULL';
264
    }
265
266
    /**
267
     * {@inheritDoc}
268
     */
269 7609
    public function getListTableForeignKeysSQL(string $table, ?string $database = null) : string
270
    {
271
        return 'SELECT quote_ident(r.conname) as conname, pg_catalog.pg_get_constraintdef(r.oid, true) as condef
272
                  FROM pg_catalog.pg_constraint r
273
                  WHERE r.conrelid =
274
                  (
275
                      SELECT c.oid
276
                      FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n
277 7609
                      WHERE ' . $this->getTableWhereClause($table) . " AND n.oid = c.relnamespace
278
                  )
279
                  AND r.contype = 'f'";
280
    }
281
282
    /**
283
     * {@inheritDoc}
284
     */
285 2536
    public function getCreateViewSQL(string $name, string $sql) : string
286
    {
287 2536
        return 'CREATE VIEW ' . $name . ' AS ' . $sql;
288
    }
289
290
    /**
291
     * {@inheritDoc}
292
     */
293 2536
    public function getDropViewSQL(string $name) : string
294
    {
295 2536
        return 'DROP VIEW ' . $name;
296
    }
297
298
    /**
299
     * {@inheritDoc}
300
     */
301 6478
    public function getListTableConstraintsSQL(string $table) : string
302
    {
303 6478
        $table = new Identifier($table);
304 6478
        $table = $this->quoteStringLiteral($table->getName());
305
306 6478
        return sprintf(
307
            <<<'SQL'
308 3
SELECT
309
    quote_ident(relname) as relname
310
FROM
311
    pg_class
312
WHERE oid IN (
313
    SELECT indexrelid
314
    FROM pg_index, pg_class
315
    WHERE pg_class.relname = %s
316
        AND pg_class.oid = pg_index.indrelid
317
        AND (indisunique = 't' OR indisprimary = 't')
318
    )
319
SQL
320
            ,
321 6478
            $table
322
        );
323
    }
324
325
    /**
326
     * {@inheritDoc}
327
     *
328
     * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html
329
     */
330 7552
    public function getListTableIndexesSQL(string $table, ?string $currentDatabase = null) : string
331
    {
332
        return 'SELECT quote_ident(relname) as relname, pg_index.indisunique, pg_index.indisprimary,
333
                       pg_index.indkey, pg_index.indrelid,
334
                       pg_get_expr(indpred, indrelid) AS where
335
                 FROM pg_class, pg_index
336
                 WHERE oid IN (
337
                    SELECT indexrelid
338
                    FROM pg_index si, pg_class sc, pg_namespace sn
339 7552
                    WHERE ' . $this->getTableWhereClause($table, 'sc', 'sn') . ' AND sc.oid=si.indrelid AND sc.relnamespace = sn.oid
340
                 ) AND pg_index.indexrelid = oid';
341
    }
342
343 7621
    private function getTableWhereClause(string $table, string $classAlias = 'c', string $namespaceAlias = 'n') : string
344
    {
345 7621
        $whereClause = $namespaceAlias . ".nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast') AND ";
346 7621
        if (strpos($table, '.') !== false) {
347 7533
            [$schema, $table] = explode('.', $table);
348 7533
            $schema           = $this->quoteStringLiteral($schema);
349
        } else {
350 7612
            $schema = "ANY(string_to_array((select replace(replace(setting,'\"\$user\"',user),' ','') from pg_catalog.pg_settings where name = 'search_path'),','))";
351
        }
352
353 7621
        $table = new Identifier($table);
354 7621
        $table = $this->quoteStringLiteral($table->getName());
355
356 7621
        return $whereClause . sprintf(
357 18
            '%s.relname = %s AND %s.nspname = %s',
358 7621
            $classAlias,
359 7621
            $table,
360 7621
            $namespaceAlias,
361 7621
            $schema
362
        );
363
    }
364
365
    /**
366
     * {@inheritDoc}
367
     */
368 7514
    public function getListTableColumnsSQL(string $table, ?string $database = null) : string
369
    {
370
        return "SELECT
371
                    a.attnum,
372
                    quote_ident(a.attname) AS field,
373
                    t.typname AS type,
374
                    format_type(a.atttypid, a.atttypmod) AS complete_type,
375
                    (SELECT tc.collcollate FROM pg_catalog.pg_collation tc WHERE tc.oid = a.attcollation) AS collation,
376
                    (SELECT t1.typname FROM pg_catalog.pg_type t1 WHERE t1.oid = t.typbasetype) AS domain_type,
377
                    (SELECT format_type(t2.typbasetype, t2.typtypmod) FROM
378
                      pg_catalog.pg_type t2 WHERE t2.typtype = 'd' AND t2.oid = a.atttypid) AS domain_complete_type,
379
                    a.attnotnull AS isnotnull,
380
                    (SELECT 't'
381
                     FROM pg_index
382
                     WHERE c.oid = pg_index.indrelid
383
                        AND pg_index.indkey[0] = a.attnum
384
                        AND pg_index.indisprimary = 't'
385
                    ) AS pri,
386
                    (SELECT pg_get_expr(adbin, adrelid)
387
                     FROM pg_attrdef
388
                     WHERE c.oid = pg_attrdef.adrelid
389
                        AND pg_attrdef.adnum=a.attnum
390
                    ) AS default,
391
                    (SELECT pg_description.description
392
                        FROM pg_description WHERE pg_description.objoid = c.oid AND a.attnum = pg_description.objsubid
393
                    ) AS comment
394
                    FROM pg_attribute a, pg_class c, pg_type t, pg_namespace n
395 7514
                    WHERE " . $this->getTableWhereClause($table, 'c', 'n') . '
396
                        AND a.attnum > 0
397
                        AND a.attrelid = c.oid
398
                        AND a.atttypid = t.oid
399
                        AND n.oid = c.relnamespace
400
                    ORDER BY a.attnum';
401
    }
402
403
    /**
404
     * {@inheritDoc}
405
     */
406 8760
    public function getCreateDatabaseSQL(string $database) : string
407
    {
408 8760
        return 'CREATE DATABASE ' . $database;
409
    }
410
411
    /**
412
     * Returns the SQL statement for disallowing new connections on the given database.
413
     *
414
     * This is useful to force DROP DATABASE operations which could fail because of active connections.
415
     *
416
     * @param string $database The name of the database to disallow new connections for.
417
     */
418 7458
    public function getDisallowDatabaseConnectionsSQL(string $database) : string
419
    {
420 7458
        return "UPDATE pg_database SET datallowconn = 'false' WHERE datname = " . $this->quoteStringLiteral($database);
421
    }
422
423
    /**
424
     * Returns the SQL statement for closing currently active connections on the given database.
425
     *
426
     * This is useful to force DROP DATABASE operations which could fail because of active connections.
427
     *
428
     * @param string $database The name of the database to close currently active connections for.
429
     */
430 8126
    public function getCloseActiveDatabaseConnectionsSQL(string $database) : string
431
    {
432
        return 'SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '
433 8126
            . $this->quoteStringLiteral($database);
434
    }
435
436
    /**
437
     * {@inheritDoc}
438
     */
439 8565
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) : string
440
    {
441 8565
        $query = '';
442
443 8565
        if ($foreignKey->hasOption('match')) {
444 7403
            $query .= ' MATCH ' . $foreignKey->getOption('match');
445
        }
446
447 8565
        $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
448
449 8565
        if ($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false) {
450 7403
            $query .= ' DEFERRABLE';
451
        } else {
452 8565
            $query .= ' NOT DEFERRABLE';
453
        }
454
455 8565
        if ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) {
456 7403
            $query .= ' INITIALLY DEFERRED';
457
        } else {
458 8565
            $query .= ' INITIALLY IMMEDIATE';
459
        }
460
461 8565
        return $query;
462
    }
463
464
    /**
465
     * {@inheritDoc}
466
     */
467 7821
    public function getAlterTableSQL(TableDiff $diff) : array
468
    {
469 7821
        $sql         = [];
470 7821
        $commentsSQL = [];
471 7821
        $columnSql   = [];
472
473 7821
        foreach ($diff->addedColumns as $column) {
474 6959
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
475
                continue;
476
            }
477
478 6959
            $query = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
479 6959
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
480
481 6959
            $comment = $this->getColumnComment($column);
482
483 6959
            if ($comment === null || $comment === '') {
484 6956
                continue;
485
            }
486
487 5903
            $commentsSQL[] = $this->getCommentOnColumnSQL(
488 5903
                $diff->getName($this)->getQuotedName($this),
489 5903
                $column->getQuotedName($this),
490 3
                $comment
491
            );
492
        }
493
494 7821
        foreach ($diff->removedColumns as $column) {
495 7548
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
496
                continue;
497
            }
498
499 7548
            $query = 'DROP ' . $column->getQuotedName($this);
500 7548
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
501
        }
502
503 7821
        foreach ($diff->changedColumns as $columnDiff) {
504
            /** @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */
505 7797
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
506
                continue;
507
            }
508
509 7797
            if ($this->isUnchangedBinaryColumn($columnDiff)) {
510 6678
                continue;
511
            }
512
513 7794
            $oldColumnName = $columnDiff->getOldColumnName()->getQuotedName($this);
514 7794
            $column        = $columnDiff->column;
515
516 7794
            if ($columnDiff->hasChanged('type') || $columnDiff->hasChanged('precision') || $columnDiff->hasChanged('scale') || $columnDiff->hasChanged('fixed')) {
517 7672
                $type = $column->getType();
518
519
                // SERIAL/BIGSERIAL are not "real" types and we can't alter a column to that type
520 7672
                $columnDefinition                  = $column->toArray();
521 7672
                $columnDefinition['autoincrement'] = false;
522
523
                // here was a server version check before, but DBAL API does not support this anymore.
524 7672
                $query = 'ALTER ' . $oldColumnName . ' TYPE ' . $type->getSQLDeclaration($columnDefinition, $this);
525 7672
                $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
526
            }
527
528 7794
            if ($columnDiff->hasChanged('default') || $this->typeChangeBreaksDefaultValue($columnDiff)) {
529 7352
                $defaultClause = $column->getDefault() === null
530 7331
                    ? ' DROP DEFAULT'
531 7352
                    : ' SET' . $this->getDefaultValueDeclarationSQL($column->toArray());
532 7352
                $query         = 'ALTER ' . $oldColumnName . $defaultClause;
533 7352
                $sql[]         = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
534
            }
535
536 7794
            if ($columnDiff->hasChanged('notnull')) {
537 6006
                $query = 'ALTER ' . $oldColumnName . ' ' . ($column->getNotnull() ? 'SET' : 'DROP') . ' NOT NULL';
538 6006
                $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
539
            }
540
541 7794
            if ($columnDiff->hasChanged('autoincrement')) {
542 2596
                if ($column->getAutoincrement()) {
543
                    // add autoincrement
544 2596
                    $seqName = $this->getIdentitySequenceName($diff->name, $oldColumnName);
545
546 2596
                    $sql[] = 'CREATE SEQUENCE ' . $seqName;
547 2596
                    $sql[] = "SELECT setval('" . $seqName . "', (SELECT MAX(" . $oldColumnName . ') FROM ' . $diff->getName($this)->getQuotedName($this) . '))';
548 2596
                    $query = 'ALTER ' . $oldColumnName . " SET DEFAULT nextval('" . $seqName . "')";
549 2596
                    $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
550
                } else {
551
                    // Drop autoincrement, but do NOT drop the sequence. It might be re-used by other tables or have
552 2590
                    $query = 'ALTER ' . $oldColumnName . ' DROP DEFAULT';
553 2590
                    $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
554
                }
555
            }
556
557 7794
            $newComment = $this->getColumnComment($column);
558 7794
            $oldComment = $this->getOldColumnComment($columnDiff);
559
560 7794
            if ($columnDiff->hasChanged('comment') || ($columnDiff->fromColumn !== null && $oldComment !== $newComment)) {
561 7389
                $commentsSQL[] = $this->getCommentOnColumnSQL(
562 7389
                    $diff->getName($this)->getQuotedName($this),
563 7389
                    $column->getQuotedName($this),
564 9
                    $newComment
565
                );
566
            }
567
568 7794
            if (! $columnDiff->hasChanged('length')) {
569 7791
                continue;
570
            }
571
572 5328
            $query = 'ALTER ' . $oldColumnName . ' TYPE ' . $column->getType()->getSQLDeclaration($column->toArray(), $this);
573 5328
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . $query;
574
        }
575
576 7821
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
577 6891
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
578
                continue;
579
            }
580
581 6891
            $oldColumnName = new Identifier($oldColumnName);
582
583 6891
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) .
584 6891
                ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this);
585
        }
586
587 7821
        $tableSql = [];
588
589 7821
        if (! $this->onSchemaAlterTable($diff, $tableSql)) {
590 7821
            $sql = array_merge($sql, $commentsSQL);
591
592 7821
            $newName = $diff->getNewName();
593
594 7821
            if ($newName !== null) {
595 6006
                $sql[] = sprintf(
596 6
                    'ALTER TABLE %s RENAME TO %s',
597 6006
                    $diff->getName($this)->getQuotedName($this),
598 6006
                    $newName->getQuotedName($this)
599
                );
600
            }
601
602 7821
            $sql = array_merge(
603 7821
                $this->getPreAlterTableIndexForeignKeySQL($diff),
604 7821
                $sql,
605 7821
                $this->getPostAlterTableIndexForeignKeySQL($diff)
606
            );
607
        }
608
609 7821
        return array_merge($sql, $tableSql, $columnSql);
610
    }
611
612
    /**
613
     * Checks whether a given column diff is a logically unchanged binary type column.
614
     *
615
     * Used to determine whether a column alteration for a binary type column can be skipped.
616
     * Doctrine's {@link \Doctrine\DBAL\Types\BinaryType} and {@link \Doctrine\DBAL\Types\BlobType}
617
     * are mapped to the same database column type on this platform as this platform
618
     * does not have a native VARBINARY/BINARY column type. Therefore the {@link \Doctrine\DBAL\Schema\Comparator}
619
     * might detect differences for binary type columns which do not have to be propagated
620
     * to database as there actually is no difference at database level.
621
     *
622
     * @param ColumnDiff $columnDiff The column diff to check against.
623
     *
624
     * @return bool True if the given column diff is an unchanged binary type column, false otherwise.
625
     */
626 7797
    private function isUnchangedBinaryColumn(ColumnDiff $columnDiff) : bool
627
    {
628 7797
        $columnType = $columnDiff->column->getType();
629
630 7797
        if (! $columnType instanceof BinaryType && ! $columnType instanceof BlobType) {
631 7794
            return false;
632
        }
633
634 6678
        $fromColumn = $columnDiff->fromColumn;
635
636 6678
        if ($fromColumn !== null) {
637 6678
            $fromColumnType = $fromColumn->getType();
638
639 6678
            if (! $fromColumnType instanceof BinaryType && ! $fromColumnType instanceof BlobType) {
640
                return false;
641
            }
642
643 6678
            return count(array_diff($columnDiff->changedProperties, ['type', 'length', 'fixed'])) === 0;
644
        }
645
646
        if ($columnDiff->hasChanged('type')) {
647
            return false;
648
        }
649
650
        return count(array_diff($columnDiff->changedProperties, ['length', 'fixed'])) === 0;
651
    }
652
653
    /**
654
     * {@inheritdoc}
655
     */
656 6506
    protected function getRenameIndexSQL(string $oldIndexName, Index $index, string $tableName) : array
657
    {
658 6506
        if (strpos($tableName, '.') !== false) {
659 5306
            [$schema]     = explode('.', $tableName);
660 5306
            $oldIndexName = $schema . '.' . $oldIndexName;
661
        }
662
663 6506
        return ['ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)];
664
    }
665
666
    /**
667
     * {@inheritdoc}
668
     */
669 7404
    public function getCommentOnColumnSQL(string $tableName, string $columnName, ?string $comment) : string
670
    {
671 7404
        $tableName  = new Identifier($tableName);
672 7404
        $columnName = new Identifier($columnName);
673 7404
        $comment    = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment);
674
675 7404
        return sprintf(
676 24
            'COMMENT ON COLUMN %s.%s IS %s',
677 7404
            $tableName->getQuotedName($this),
678 7404
            $columnName->getQuotedName($this),
679 7404
            $comment
680
        );
681
    }
682
683
    /**
684
     * {@inheritDoc}
685
     */
686 7873
    public function getCreateSequenceSQL(Sequence $sequence) : string
687
    {
688 7873
        return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) .
689 7873
            ' INCREMENT BY ' . $sequence->getAllocationSize() .
690 7873
            ' MINVALUE ' . $sequence->getInitialValue() .
691 7873
            ' START ' . $sequence->getInitialValue() .
692 7873
            $this->getSequenceCacheSQL($sequence);
693
    }
694
695
    /**
696
     * {@inheritDoc}
697
     */
698
    public function getAlterSequenceSQL(Sequence $sequence) : string
699
    {
700
        return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) .
701
            ' INCREMENT BY ' . $sequence->getAllocationSize() .
702
            $this->getSequenceCacheSQL($sequence);
703
    }
704
705
    /**
706
     * Cache definition for sequences
707
     */
708 7873
    private function getSequenceCacheSQL(Sequence $sequence) : string
709
    {
710 7873
        if ($sequence->getCache() > 1) {
711 6703
            return ' CACHE ' . $sequence->getCache();
712
        }
713
714 7870
        return '';
715
    }
716
717
    /**
718
     * {@inheritDoc}
719
     */
720 7870
    public function getDropSequenceSQL($sequence) : string
721
    {
722 7870
        if ($sequence instanceof Sequence) {
723
            $sequence = $sequence->getQuotedName($this);
724
        }
725
726 7870
        return 'DROP SEQUENCE ' . $sequence . ' CASCADE';
727
    }
728
729
    /**
730
     * {@inheritDoc}
731
     */
732 7643
    public function getCreateSchemaSQL(string $schemaName) : string
733
    {
734 7643
        return 'CREATE SCHEMA ' . $schemaName;
735
    }
736
737
    /**
738
     * {@inheritDoc}
739
     */
740 7515
    public function getDropForeignKeySQL($foreignKey, $table) : string
741
    {
742 7515
        return $this->getDropConstraintSQL($foreignKey, $table);
743
    }
744
745
    /**
746
     * {@inheritDoc}
747
     */
748 8780
    protected function _getCreateTableSQL(string $tableName, array $columns, array $options = []) : array
749
    {
750 8780
        $queryFields = $this->getColumnDeclarationListSQL($columns);
751
752 8780
        if (isset($options['primary']) && ! empty($options['primary'])) {
753 7876
            $keyColumns   = array_unique(array_values($options['primary']));
754 7876
            $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
755
        }
756
757 8780
        $query = 'CREATE TABLE ' . $tableName . ' (' . $queryFields . ')';
758
759 8780
        $sql = [$query];
760
761 8780
        if (isset($options['indexes']) && ! empty($options['indexes'])) {
762 7590
            foreach ($options['indexes'] as $index) {
763 7590
                $sql[] = $this->getCreateIndexSQL($index, $tableName);
764
            }
765
        }
766
767 8780
        if (isset($options['foreignKeys'])) {
768 7506
            foreach ((array) $options['foreignKeys'] as $definition) {
769 7242
                $sql[] = $this->getCreateForeignKeySQL($definition, $tableName);
770
            }
771
        }
772
773 8780
        return $sql;
774
    }
775
776
    /**
777
     * Converts a single boolean value.
778
     *
779
     * First converts the value to its native PHP boolean type
780
     * and passes it to the given callback function to be reconverted
781
     * into any custom representation.
782
     *
783
     * @param mixed    $value    The value to convert.
784
     * @param callable $callback The callback function to use for converting the real boolean value.
785
     *
786
     * @return mixed
787
     *
788
     * @throws UnexpectedValueException
789
     */
790 8093
    private function convertSingleBooleanValue($value, callable $callback)
791
    {
792 8093
        if ($value === null) {
793 2023
            return $callback(null);
794
        }
795
796 8093
        if (is_bool($value) || is_numeric($value)) {
797 8041
            return $callback((bool) $value);
798
        }
799
800 7445
        if (! is_string($value)) {
801
            return $callback(true);
802
        }
803
804
        /**
805
         * Better safe than sorry: http://php.net/in_array#106319
806
         */
807 7445
        if (in_array(strtolower(trim($value)), $this->booleanLiterals['false'], true)) {
808 7389
            return $callback(false);
809
        }
810
811 7093
        if (in_array(strtolower(trim($value)), $this->booleanLiterals['true'], true)) {
812 7090
            return $callback(true);
813
        }
814
815 6853
        throw new UnexpectedValueException(sprintf(
816 3
            'Unrecognized boolean literal, %s given.',
817 6853
            $value
818
        ));
819
    }
820
821
    /**
822
     * Converts one or multiple boolean values.
823
     *
824
     * First converts the value(s) to their native PHP boolean type
825
     * and passes them to the given callback function to be reconverted
826
     * into any custom representation.
827
     *
828
     * @param mixed    $item     The value(s) to convert.
829
     * @param callable $callback The callback function to use for converting the real boolean value(s).
830
     *
831
     * @return mixed
832
     */
833 8093
    private function doConvertBooleans($item, callable $callback)
834
    {
835 8093
        if (is_array($item)) {
836
            foreach ($item as $key => $value) {
837
                $item[$key] = $this->convertSingleBooleanValue($value, $callback);
838
            }
839
840
            return $item;
841
        }
842
843 8093
        return $this->convertSingleBooleanValue($item, $callback);
844
    }
845
846
    /**
847
     * {@inheritDoc}
848
     *
849
     * Postgres wants boolean values converted to the strings 'true'/'false'.
850
     */
851 8060
    public function convertBooleans($item)
852
    {
853 8060
        if (! $this->useBooleanTrueFalseStrings) {
854 6981
            return parent::convertBooleans($item);
855
        }
856
857 8054
        return $this->doConvertBooleans(
858 8054
            $item,
859
            static function ($boolean) : string {
860 8054
                if ($boolean === null) {
861
                    return 'NULL';
862
                }
863
864 8054
                return $boolean === true ? 'true' : 'false';
865 8054
            }
866
        );
867
    }
868
869
    /**
870
     * {@inheritDoc}
871
     */
872 7359
    public function convertBooleansToDatabaseValue($item)
873
    {
874 7359
        if (! $this->useBooleanTrueFalseStrings) {
875 6928
            return parent::convertBooleansToDatabaseValue($item);
876
        }
877
878 7356
        return $this->doConvertBooleans(
879 7356
            $item,
880
            static function ($boolean) : ?int {
881 7353
                return $boolean === null ? null : (int) $boolean;
882 7356
            }
883
        );
884
    }
885
886
    /**
887
     * {@inheritDoc}
888
     */
889 7285
    public function convertFromBoolean($item) : ?bool
890
    {
891 7285
        if (in_array($item, $this->booleanLiterals['false'], true)) {
892 6893
            return false;
893
        }
894
895 7267
        return parent::convertFromBoolean($item);
896
    }
897
898
    /**
899
     * {@inheritDoc}
900
     */
901 7253
    public function getSequenceNextValSQL(string $sequenceName) : string
902
    {
903 7253
        return "SELECT NEXTVAL('" . $sequenceName . "')";
904
    }
905
906
    /**
907
     * {@inheritDoc}
908
     */
909 7353
    public function getSetTransactionIsolationSQL(int $level) : string
910
    {
911
        return 'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL '
912 7353
            . $this->_getTransactionIsolationLevelSQL($level);
913
    }
914
915
    /**
916
     * {@inheritDoc}
917
     */
918 7201
    public function getBooleanTypeDeclarationSQL(array $columnDef) : string
919
    {
920 7201
        return 'BOOLEAN';
921
    }
922
923
    /**
924
     * {@inheritDoc}
925
     */
926 8771
    public function getIntegerTypeDeclarationSQL(array $columnDef) : string
927
    {
928 8771
        if (! empty($columnDef['autoincrement'])) {
929 8423
            return 'SERIAL';
930
        }
931
932 8626
        return 'INT';
933
    }
934
935
    /**
936
     * {@inheritDoc}
937
     */
938 8035
    public function getBigIntTypeDeclarationSQL(array $columnDef) : string
939
    {
940 8035
        if (! empty($columnDef['autoincrement'])) {
941 8023
            return 'BIGSERIAL';
942
        }
943
944 2519
        return 'BIGINT';
945
    }
946
947
    /**
948
     * {@inheritDoc}
949
     */
950 8184
    public function getSmallIntTypeDeclarationSQL(array $columnDef) : string
951
    {
952 8184
        if (! empty($columnDef['autoincrement'])) {
953 7453
            return 'SMALLSERIAL';
954
        }
955
956 8184
        return 'SMALLINT';
957
    }
958
959
    /**
960
     * {@inheritDoc}
961
     */
962 6653
    public function getGuidTypeDeclarationSQL(array $column) : string
963
    {
964 6653
        return 'UUID';
965
    }
966
967
    /**
968
     * {@inheritDoc}
969
     */
970 8161
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) : string
971
    {
972 8161
        return 'TIMESTAMP(0) WITHOUT TIME ZONE';
973
    }
974
975
    /**
976
     * {@inheritDoc}
977
     */
978 2005
    public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) : string
979
    {
980 2005
        return 'TIMESTAMP(0) WITH TIME ZONE';
981
    }
982
983
    /**
984
     * {@inheritDoc}
985
     */
986 2758
    public function getDateTypeDeclarationSQL(array $fieldDeclaration) : string
987
    {
988 2758
        return 'DATE';
989
    }
990
991
    /**
992
     * {@inheritDoc}
993
     */
994 2752
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration) : string
995
    {
996 2752
        return 'TIME(0) WITHOUT TIME ZONE';
997
    }
998
999
    /**
1000
     * {@inheritDoc}
1001
     */
1002
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) : string
1003
    {
1004
        return '';
1005
    }
1006
1007
    /**
1008
     * {@inheritDoc}
1009
     */
1010 7852
    protected function getVarcharTypeDeclarationSQLSnippet(?int $length) : string
1011
    {
1012 7852
        $sql = 'VARCHAR';
1013
1014 7852
        if ($length !== null) {
1015 7849
            $sql .= sprintf('(%d)', $length);
1016
        }
1017
1018 7852
        return $sql;
1019
    }
1020
1021
    /**
1022
     * {@inheritDoc}
1023
     */
1024 6734
    protected function getBinaryTypeDeclarationSQLSnippet(?int $length) : string
1025
    {
1026 6734
        return 'BYTEA';
1027
    }
1028
1029
    /**
1030
     * {@inheritDoc}
1031
     */
1032 6696
    protected function getVarbinaryTypeDeclarationSQLSnippet(?int $length) : string
1033
    {
1034 6696
        return 'BYTEA';
1035
    }
1036
1037
    /**
1038
     * {@inheritDoc}
1039
     */
1040 7658
    public function getClobTypeDeclarationSQL(array $field) : string
1041
    {
1042 7658
        return 'TEXT';
1043
    }
1044
1045
    /**
1046
     * {@inheritDoc}
1047
     */
1048 7967
    public function getName() : string
1049
    {
1050 7967
        return 'postgresql';
1051
    }
1052
1053
    /**
1054
     * {@inheritDoc}
1055
     *
1056
     * PostgreSQL returns all column names in SQL result sets in lowercase.
1057
     */
1058
    public function getSQLResultCasing(string $column) : string
1059
    {
1060
        return strtolower($column);
1061
    }
1062
1063
    /**
1064
     * {@inheritDoc}
1065
     */
1066 1935
    public function getDateTimeTzFormatString() : string
1067
    {
1068 1935
        return 'Y-m-d H:i:sO';
1069
    }
1070
1071
    /**
1072
     * {@inheritDoc}
1073
     */
1074 1830
    public function getEmptyIdentityInsertSQL(string $tableName, string $identifierColumnName) : string
1075
    {
1076 1830
        return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (DEFAULT)';
1077
    }
1078
1079
    /**
1080
     * {@inheritDoc}
1081
     */
1082 7205
    public function getTruncateTableSQL(string $tableName, bool $cascade = false) : string
1083
    {
1084 7205
        $tableIdentifier = new Identifier($tableName);
1085 7205
        $sql             = 'TRUNCATE ' . $tableIdentifier->getQuotedName($this);
1086
1087 7205
        if ($cascade) {
1088
            $sql .= ' CASCADE';
1089
        }
1090
1091 7205
        return $sql;
1092
    }
1093
1094
    /**
1095
     * {@inheritDoc}
1096
     */
1097
    public function getReadLockSQL() : string
1098
    {
1099
        return 'FOR SHARE';
1100
    }
1101
1102
    /**
1103
     * {@inheritDoc}
1104
     */
1105 8400
    protected function initializeDoctrineTypeMappings() : void
1106
    {
1107 8400
        $this->doctrineTypeMapping = [
1108
            'bigint'           => 'bigint',
1109
            'bigserial'        => 'bigint',
1110
            'bool'             => 'boolean',
1111
            'boolean'          => 'boolean',
1112
            'bpchar'           => 'string',
1113
            'bytea'            => 'blob',
1114
            'char'             => 'string',
1115
            'date'             => 'date',
1116
            'datetime'         => 'datetime',
1117
            'decimal'          => 'decimal',
1118
            'double'           => 'float',
1119
            'double precision' => 'float',
1120
            'float'            => 'float',
1121
            'float4'           => 'float',
1122
            'float8'           => 'float',
1123
            'inet'             => 'string',
1124
            'int'              => 'integer',
1125
            'int2'             => 'smallint',
1126
            'int4'             => 'integer',
1127
            'int8'             => 'bigint',
1128
            'integer'          => 'integer',
1129
            'interval'         => 'string',
1130
            'json'             => Type::JSON,
0 ignored issues
show
Deprecated Code introduced by
The constant Doctrine\DBAL\Types\Type::JSON has been deprecated: Use {@see DefaultTypes::JSON} instead. ( Ignorable by Annotation )

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

1130
            'json'             => /** @scrutinizer ignore-deprecated */ Type::JSON,

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
1131
            'money'            => 'decimal',
1132
            'numeric'          => 'decimal',
1133
            'serial'           => 'integer',
1134
            'serial4'          => 'integer',
1135
            'serial8'          => 'bigint',
1136
            'real'             => 'float',
1137
            'smallint'         => 'smallint',
1138
            'text'             => 'text',
1139
            'time'             => 'time',
1140
            'timestamp'        => 'datetime',
1141
            'timestamptz'      => 'datetimetz',
1142
            'timetz'           => 'time',
1143
            'tsvector'         => 'text',
1144
            'uuid'             => 'guid',
1145
            'varchar'          => 'string',
1146
            'year'             => 'date',
1147
            '_varchar'         => 'string',
1148
        ];
1149 8400
    }
1150
1151
    /**
1152
     * {@inheritdoc}
1153
     */
1154 9021
    public function hasNativeJsonType() : bool
1155
    {
1156 9021
        return true;
1157
    }
1158
1159
    /**
1160
     * {@inheritDoc}
1161
     */
1162 2911
    protected function getReservedKeywordsClass() : string
1163
    {
1164 2911
        return Keywords\PostgreSQLKeywords::class;
1165
    }
1166
1167
    /**
1168
     * {@inheritDoc}
1169
     */
1170 3190
    public function getBlobTypeDeclarationSQL(array $field) : string
1171
    {
1172 3190
        return 'BYTEA';
1173
    }
1174
1175
    /**
1176
     * {@inheritdoc}
1177
     */
1178 8810
    public function getDefaultValueDeclarationSQL(array $field) : string
1179
    {
1180 8810
        if ($this->isSerialField($field)) {
1181 8432
            return '';
1182
        }
1183
1184 7915
        return parent::getDefaultValueDeclarationSQL($field);
1185
    }
1186
1187
    /**
1188
     * {@inheritdoc}
1189
     */
1190
    public function supportsColumnCollation() : bool
1191
    {
1192
        return true;
1193
    }
1194
1195
    /**
1196
     * {@inheritdoc}
1197
     */
1198 7503
    public function getColumnCollationDeclarationSQL(string $collation) : string
1199
    {
1200 7503
        return 'COLLATE ' . $this->quoteSingleIdentifier($collation);
1201
    }
1202
1203
    /**
1204
     * {@inheritdoc}
1205
     */
1206 2760
    public function getJsonTypeDeclarationSQL(array $field) : string
1207
    {
1208 2760
        return 'JSON';
1209
    }
1210
1211
    /**
1212
     * @param mixed[] $field
1213
     */
1214 8810
    private function isSerialField(array $field) : bool
1215
    {
1216 8810
        return isset($field['type'], $field['autoincrement'])
1217 8810
            && $field['autoincrement'] === true
1218 8810
            && $this->isNumericType($field['type']);
1219
    }
1220
1221
    /**
1222
     * Check whether the type of a column is changed in a way that invalidates the default value for the column
1223
     */
1224 7791
    private function typeChangeBreaksDefaultValue(ColumnDiff $columnDiff) : bool
1225
    {
1226 7791
        if ($columnDiff->fromColumn === null) {
1227 6815
            return $columnDiff->hasChanged('type');
1228
        }
1229
1230 7643
        $oldTypeIsNumeric = $this->isNumericType($columnDiff->fromColumn->getType());
1231 7643
        $newTypeIsNumeric = $this->isNumericType($columnDiff->column->getType());
1232
1233
        // default should not be changed when switching between numeric types and the default comes from a sequence
1234 7643
        return $columnDiff->hasChanged('type')
1235 7643
            && ! ($oldTypeIsNumeric && $newTypeIsNumeric && $columnDiff->column->getAutoincrement());
1236
    }
1237
1238 8444
    private function isNumericType(Type $type) : bool
1239
    {
1240 8444
        return $type instanceof IntegerType || $type instanceof BigIntType;
1241
    }
1242
1243 7794
    private function getOldColumnComment(ColumnDiff $columnDiff) : ?string
1244
    {
1245 7794
        if ($columnDiff->fromColumn === null) {
1246 6818
            return null;
1247
        }
1248
1249 7643
        return $this->getColumnComment($columnDiff->fromColumn);
1250
    }
1251
}
1252