Failed Conditions
Push — master ( 656579...2742cd )
by Marco
11:55
created

DB2Platform   F

Complexity

Total Complexity 121

Size/Duplication

Total Lines 850
Duplicated Lines 7.06 %

Test Coverage

Coverage 89.69%

Importance

Changes 0
Metric Value
wmc 121
dl 60
loc 850
ccs 235
cts 262
cp 0.8969
rs 1.0434
c 0
b 0
f 0

58 Methods

Rating   Name   Duplication   Size   Complexity  
A getDropViewSQL() 0 3 1
A isCommentedDoctrineType() 0 9 2
A prefersIdentityColumns() 0 3 1
A _getCreateTableSQL() 0 14 3
A getBinaryTypeDeclarationSQLSnippet() 0 3 4
A getSQLResultCasing() 0 3 1
B getDefaultValueDeclarationSQL() 0 13 5
A getBlobTypeDeclarationSQL() 0 4 1
A getBitOrComparisonExpression() 0 3 1
A initializeDoctrineTypeMappings() 0 18 1
A getIntegerTypeDeclarationSQL() 0 3 1
A getCurrentTimeSQL() 0 3 1
A getListViewsSQL() 0 3 1
A getClobTypeDeclarationSQL() 0 4 1
A supportsSavepoints() 0 3 1
A getBinaryMaxLength() 0 3 1
A getForUpdateSQL() 0 3 1
A getDateDiffExpression() 0 3 1
A getName() 0 3 1
A getDummySelectSQL() 0 3 1
A getTimeTypeDeclarationSQL() 0 3 1
A getEmptyIdentityInsertSQL() 0 3 1
A getSmallIntTypeDeclarationSQL() 0 3 1
B getListTableForeignKeysSQL() 0 28 1
A getCurrentDateSQL() 0 3 1
A getDateTypeDeclarationSQL() 0 3 1
A getDropDatabaseSQL() 0 3 1
A getLocateExpression() 7 7 2
B getListTableColumnsSQL() 0 38 1
A supportsReleaseSavepoints() 0 3 1
A getBinaryDefaultLength() 0 3 1
A getReservedKeywordsClass() 0 3 1
A getCreateTemporaryTableSnippetSQL() 0 3 1
C getAlterColumnClausesSQL() 0 38 12
B getPreAlterTableIndexForeignKeySQL() 0 29 6
A supportsCommentOnStatement() 0 3 1
A getBitAndComparisonExpression() 0 3 1
F getAlterTableSQL() 24 101 21
A getListTableIndexesSQL() 0 18 1
A getCreateViewSQL() 0 3 1
A gatherAlterColumnSQL() 0 20 4
A getDateArithmeticIntervalExpression() 11 15 3
A getCurrentTimestampSQL() 0 3 1
A getDateTimeTypeDeclarationSQL() 0 7 3
A doModifyLimitQuery() 0 21 4
A getBooleanTypeDeclarationSQL() 0 3 1
A getListTablesSQL() 0 3 1
A getRenameIndexSQL() 8 8 2
A getSubstringExpression() 7 7 2
A supportsIdentityColumns() 0 3 1
A getBigIntTypeDeclarationSQL() 0 3 1
A getVarcharTypeDeclarationSQLSnippet() 0 4 4
A supportsCreateDropDatabase() 0 3 1
A _getCommonIntegerTypeDeclarationSQL() 0 8 2
A getTemporaryTableName() 0 3 1
A getTruncateTableSQL() 0 5 1
A getCreateDatabaseSQL() 0 3 1
A getIndexDeclarationSQL() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like DB2Platform 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 DB2Platform, and based on these observations, apply Extract Interface, too.

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\DBALException;
23
use Doctrine\DBAL\Schema\ColumnDiff;
24
use Doctrine\DBAL\Schema\Identifier;
25
use Doctrine\DBAL\Schema\Index;
26
use Doctrine\DBAL\Schema\Table;
27
use Doctrine\DBAL\Schema\TableDiff;
28
use Doctrine\DBAL\Types\Type;
29
30
class DB2Platform extends AbstractPlatform
31
{
32
    /**
33
     * {@inheritdoc}
34
     */
35 2
    public function getBinaryMaxLength()
36
    {
37 2
        return 32704;
38
    }
39
40
    /**
41
     * {@inheritdoc}
42
     */
43 2
    public function getBinaryDefaultLength()
44
    {
45 2
        return 1;
46
    }
47
48
    /**
49
     * {@inheritDoc}
50
     */
51 2
    public function getBlobTypeDeclarationSQL(array $field)
52
    {
53
        // todo blob(n) with $field['length'];
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
54 2
        return 'BLOB(1M)';
55
    }
56
57
    /**
58
     * {@inheritDoc}
59
     */
60 5
    public function initializeDoctrineTypeMappings()
61
    {
62 5
        $this->doctrineTypeMapping = [
63
            'smallint'      => 'smallint',
64
            'bigint'        => 'bigint',
65
            'integer'       => 'integer',
66
            'time'          => 'time',
67
            'date'          => 'date',
68
            'varchar'       => 'string',
69
            'character'     => 'string',
70
            'varbinary'     => 'binary',
71
            'binary'        => 'binary',
72
            'clob'          => 'text',
73
            'blob'          => 'blob',
74
            'decimal'       => 'decimal',
75
            'double'        => 'float',
76
            'real'          => 'float',
77
            'timestamp'     => 'datetime',
78
        ];
79 5
    }
80
81
    /**
82
     * {@inheritdoc}
83
     */
84 42
    public function isCommentedDoctrineType(Type $doctrineType)
85
    {
86 42
        if ($doctrineType->getName() === Type::BOOLEAN) {
87
            // We require a commented boolean type in order to distinguish between boolean and smallint
88
            // as both (have to) map to the same native type.
89 1
            return true;
90
        }
91
92 41
        return parent::isCommentedDoctrineType($doctrineType);
93
    }
94
95
    /**
96
     * {@inheritDoc}
97
     */
98 15
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
99
    {
100 15
        return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
101 15
                : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)');
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     */
107 1
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
108
    {
109 1
        return $fixed ? 'BINARY(' . ($length ?: 255) . ')' : 'VARBINARY(' . ($length ?: 255) . ')';
110
    }
111
112
    /**
113
     * {@inheritDoc}
114
     */
115 3
    public function getClobTypeDeclarationSQL(array $field)
116
    {
117
        // todo clob(n) with $field['length'];
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
118 3
        return 'CLOB(1M)';
119
    }
120
121
    /**
122
     * {@inheritDoc}
123
     */
124 3
    public function getName()
125
    {
126 3
        return 'db2';
127
    }
128
129
    /**
130
     * {@inheritDoc}
131
     */
132 2
    public function getBooleanTypeDeclarationSQL(array $columnDef)
133
    {
134 2
        return 'SMALLINT';
135
    }
136
137
    /**
138
     * {@inheritDoc}
139
     */
140 12
    public function getIntegerTypeDeclarationSQL(array $columnDef)
141
    {
142 12
        return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
143
    }
144
145
    /**
146
     * {@inheritDoc}
147
     */
148 1
    public function getBigIntTypeDeclarationSQL(array $columnDef)
149
    {
150 1
        return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
151
    }
152
153
    /**
154
     * {@inheritDoc}
155
     */
156 1
    public function getSmallIntTypeDeclarationSQL(array $columnDef)
157
    {
158 1
        return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
159
    }
160
161
    /**
162
     * {@inheritDoc}
163
     */
164 12
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
165
    {
166 12
        $autoinc = '';
167 12
        if ( ! empty($columnDef['autoincrement'])) {
168 2
            $autoinc = ' GENERATED BY DEFAULT AS IDENTITY';
169
        }
170
171 12
        return $autoinc;
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177 1
    public function getBitAndComparisonExpression($value1, $value2)
178
    {
179 1
        return 'BITAND(' . $value1 . ', ' . $value2 . ')';
180
    }
181
182
    /**
183
     * {@inheritdoc}
184
     */
185 1
    public function getBitOrComparisonExpression($value1, $value2)
186
    {
187 1
        return 'BITOR(' . $value1 . ', ' . $value2 . ')';
188
    }
189
190
    /**
191
     * {@inheritdoc}
192
     */
193 1
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
194
    {
195 View Code Duplication
        switch ($unit) {
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...
196 1
            case self::DATE_INTERVAL_UNIT_WEEK:
197 1
                $interval *= 7;
198 1
                $unit = self::DATE_INTERVAL_UNIT_DAY;
199 1
                break;
200
201 1
            case self::DATE_INTERVAL_UNIT_QUARTER:
202 1
                $interval *= 3;
203 1
                $unit = self::DATE_INTERVAL_UNIT_MONTH;
204 1
                break;
205
        }
206
207 1
        return $date . ' ' . $operator . ' ' . $interval . ' ' . $unit;
208
    }
209
210
    /**
211
     * {@inheritdoc}
212
     */
213 1
    public function getDateDiffExpression($date1, $date2)
214
    {
215 1
        return 'DAYS(' . $date1 . ') - DAYS(' . $date2 . ')';
216
    }
217
218
    /**
219
     * {@inheritDoc}
220
     */
221 1
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
222
    {
223 1
        if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) {
224 1
            return "TIMESTAMP(0) WITH DEFAULT";
225
        }
226
227 1
        return 'TIMESTAMP(0)';
228
    }
229
230
    /**
231
     * {@inheritDoc}
232
     */
233 1
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
234
    {
235 1
        return 'DATE';
236
    }
237
238
    /**
239
     * {@inheritDoc}
240
     */
241 1
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
242
    {
243 1
        return 'TIME';
244
    }
245
246
    /**
247
     * {@inheritdoc}
248
     */
249 2
    public function getTruncateTableSQL($tableName, $cascade = false)
250
    {
251 2
        $tableIdentifier = new Identifier($tableName);
252
253 2
        return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this) . ' IMMEDIATE';
254
    }
255
256
    /**
257
     * This code fragment is originally from the Zend_Db_Adapter_Db2 class, but has been edited.
258
     *
259
     * @license New BSD License
260
     *
261
     * @param string $table
262
     * @param string $database
263
     *
264
     * @return string
265
     */
266 1
    public function getListTableColumnsSQL($table, $database = null)
267
    {
268 1
        $table = $this->quoteStringLiteral($table);
269
270
        // We do the funky subquery and join syscat.columns.default this crazy way because
271
        // as of db2 v10, the column is CLOB(64k) and the distinct operator won't allow a CLOB,
272
        // it wants shorter stuff like a varchar.
273
        return "
274
        SELECT
275
          cols.default,
276
          subq.*
277
        FROM (
278
               SELECT DISTINCT
279
                 c.tabschema,
280
                 c.tabname,
281
                 c.colname,
282
                 c.colno,
283
                 c.typename,
284
                 c.nulls,
285
                 c.length,
286
                 c.scale,
287
                 c.identity,
288
                 tc.type AS tabconsttype,
289
                 c.remarks AS comment,
290
                 k.colseq,
291
                 CASE
292
                 WHEN c.generated = 'D' THEN 1
293
                 ELSE 0
294
                 END     AS autoincrement
295
               FROM syscat.columns c
296
                 LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc
297
                     ON (k.tabschema = tc.tabschema
298
                         AND k.tabname = tc.tabname
299
                         AND tc.type = 'P'))
300
                   ON (c.tabschema = k.tabschema
301
                       AND c.tabname = k.tabname
302
                       AND c.colname = k.colname)
303 1
               WHERE UPPER(c.tabname) = UPPER(" . $table . ")
304
               ORDER BY c.colno
305
             ) subq
306
          JOIN syscat.columns cols
307
            ON subq.tabschema = cols.tabschema
308
               AND subq.tabname = cols.tabname
309
               AND subq.colno = cols.colno
310
        ORDER BY subq.colno
311
        ";
312
    }
313
314
    /**
315
     * {@inheritDoc}
316
     */
317
    public function getListTablesSQL()
318
    {
319
        return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = 'T'";
320
    }
321
322
    /**
323
     * {@inheritDoc}
324
     */
325
    public function getListViewsSQL($database)
326
    {
327
        return "SELECT NAME, TEXT FROM SYSIBM.SYSVIEWS";
328
    }
329
330
    /**
331
     * {@inheritDoc}
332
     */
333 1
    public function getListTableIndexesSQL($table, $currentDatabase = null)
334
    {
335 1
        $table = $this->quoteStringLiteral($table);
336
337
        return "SELECT   idx.INDNAME AS key_name,
338
                         idxcol.COLNAME AS column_name,
339
                         CASE
340
                             WHEN idx.UNIQUERULE = 'P' THEN 1
341
                             ELSE 0
342
                         END AS primary,
343
                         CASE
344
                             WHEN idx.UNIQUERULE = 'D' THEN 1
345
                             ELSE 0
346
                         END AS non_unique
347
                FROM     SYSCAT.INDEXES AS idx
348
                JOIN     SYSCAT.INDEXCOLUSE AS idxcol
349
                ON       idx.INDSCHEMA = idxcol.INDSCHEMA AND idx.INDNAME = idxcol.INDNAME
350 1
                WHERE    idx.TABNAME = UPPER(" . $table . ")
351
                ORDER BY idxcol.COLSEQ ASC";
352
    }
353
354
    /**
355
     * {@inheritDoc}
356
     */
357 1
    public function getListTableForeignKeysSQL($table)
358
    {
359 1
        $table = $this->quoteStringLiteral($table);
360
361
        return "SELECT   fkcol.COLNAME AS local_column,
362
                         fk.REFTABNAME AS foreign_table,
363
                         pkcol.COLNAME AS foreign_column,
364
                         fk.CONSTNAME AS index_name,
365
                         CASE
366
                             WHEN fk.UPDATERULE = 'R' THEN 'RESTRICT'
367
                             ELSE NULL
368
                         END AS on_update,
369
                         CASE
370
                             WHEN fk.DELETERULE = 'C' THEN 'CASCADE'
371
                             WHEN fk.DELETERULE = 'N' THEN 'SET NULL'
372
                             WHEN fk.DELETERULE = 'R' THEN 'RESTRICT'
373
                             ELSE NULL
374
                         END AS on_delete
375
                FROM     SYSCAT.REFERENCES AS fk
376
                JOIN     SYSCAT.KEYCOLUSE AS fkcol
377
                ON       fk.CONSTNAME = fkcol.CONSTNAME
378
                AND      fk.TABSCHEMA = fkcol.TABSCHEMA
379
                AND      fk.TABNAME = fkcol.TABNAME
380
                JOIN     SYSCAT.KEYCOLUSE AS pkcol
381
                ON       fk.REFKEYNAME = pkcol.CONSTNAME
382
                AND      fk.REFTABSCHEMA = pkcol.TABSCHEMA
383
                AND      fk.REFTABNAME = pkcol.TABNAME
384 1
                WHERE    fk.TABNAME = UPPER(" . $table . ")
385
                ORDER BY fkcol.COLSEQ ASC";
386
    }
387
388
    /**
389
     * {@inheritDoc}
390
     */
391 1
    public function getCreateViewSQL($name, $sql)
392
    {
393 1
        return "CREATE VIEW ".$name." AS ".$sql;
394
    }
395
396
    /**
397
     * {@inheritDoc}
398
     */
399 1
    public function getDropViewSQL($name)
400
    {
401 1
        return "DROP VIEW ".$name;
402
    }
403
404
    /**
405
     * {@inheritDoc}
406
     */
407 1
    public function getCreateDatabaseSQL($database)
408
    {
409 1
        return "CREATE DATABASE ".$database;
410
    }
411
412
    /**
413
     * {@inheritDoc}
414
     */
415 1
    public function getDropDatabaseSQL($database)
416
    {
417 1
        return "DROP DATABASE " . $database;
418
    }
419
420
    /**
421
     * {@inheritDoc}
422
     */
423 1
    public function supportsCreateDropDatabase()
424
    {
425 1
        return false;
426
    }
427
428
    /**
429
     * {@inheritDoc}
430
     */
431 1
    public function supportsReleaseSavepoints()
432
    {
433 1
        return false;
434
    }
435
436
    /**
437
     * {@inheritdoc}
438
     */
439 13
    public function supportsCommentOnStatement()
440
    {
441 13
        return true;
442
    }
443
444
    /**
445
     * {@inheritDoc}
446
     */
447 2
    public function getCurrentDateSQL()
448
    {
449 2
        return 'CURRENT DATE';
450
    }
451
452
    /**
453
     * {@inheritDoc}
454
     */
455 1
    public function getCurrentTimeSQL()
456
    {
457 1
        return 'CURRENT TIME';
458
    }
459
460
    /**
461
     * {@inheritDoc}
462
     */
463 2
    public function getCurrentTimestampSQL()
464
    {
465 2
        return "CURRENT TIMESTAMP";
466
    }
467
468
    /**
469
     * {@inheritDoc}
470
     */
471 1
    public function getIndexDeclarationSQL($name, Index $index)
472
    {
473
        // Index declaration in statements like CREATE TABLE is not supported.
474 1
        throw DBALException::notSupported(__METHOD__);
475
    }
476
477
    /**
478
     * {@inheritDoc}
479
     */
480 12
    protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
481
    {
482 12
        $indexes = [];
483 12
        if (isset($options['indexes'])) {
484 12
            $indexes = $options['indexes'];
485
        }
486 12
        $options['indexes'] = [];
487
488 12
        $sqls = parent::_getCreateTableSQL($tableName, $columns, $options);
489
490 12
        foreach ($indexes as $definition) {
491 4
            $sqls[] = $this->getCreateIndexSQL($definition, $tableName);
492
        }
493 12
        return $sqls;
494
    }
495
496
    /**
497
     * {@inheritDoc}
498
     */
499 24
    public function getAlterTableSQL(TableDiff $diff)
500
    {
501 24
        $sql = [];
502 24
        $columnSql = [];
503 24
        $commentsSQL = [];
504
505 24
        $queryParts = [];
506 24
        foreach ($diff->addedColumns as $column) {
507 4
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
508
                continue;
509
            }
510
511 4
            $columnDef = $column->toArray();
512 4
            $queryPart = 'ADD COLUMN ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef);
513
514
            // Adding non-nullable columns to a table requires a default value to be specified.
515 4
            if ( ! empty($columnDef['notnull']) &&
516 4
                ! isset($columnDef['default']) &&
517 4
                empty($columnDef['autoincrement'])
518
            ) {
519 3
                $queryPart .= ' WITH DEFAULT';
520
            }
521
522 4
            $queryParts[] = $queryPart;
523
524 4
            $comment = $this->getColumnComment($column);
525
526 4 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...
527 1
                $commentsSQL[] = $this->getCommentOnColumnSQL(
528 1
                    $diff->getName($this)->getQuotedName($this),
529 1
                    $column->getQuotedName($this),
530 4
                    $comment
531
                );
532
            }
533
        }
534
535 24 View Code Duplication
        foreach ($diff->removedColumns as $column) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
536 3
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
537
                continue;
538
            }
539
540 3
            $queryParts[] =  'DROP COLUMN ' . $column->getQuotedName($this);
541
        }
542
543 24
        foreach ($diff->changedColumns as $columnDiff) {
544 17
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
545
                continue;
546
            }
547
548 17
            if ($columnDiff->hasChanged('comment')) {
549 1
                $commentsSQL[] = $this->getCommentOnColumnSQL(
550 1
                    $diff->getName($this)->getQuotedName($this),
551 1
                    $columnDiff->column->getQuotedName($this),
552 1
                    $this->getColumnComment($columnDiff->column)
553
                );
554
555 1
                if (count($columnDiff->changedProperties) === 1) {
556 1
                    continue;
557
                }
558
            }
559
560 16
            $this->gatherAlterColumnSQL($diff->fromTable, $columnDiff, $sql, $queryParts);
561
        }
562
563 24 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...
564 4
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
565
                continue;
566
            }
567
568 4
            $oldColumnName = new Identifier($oldColumnName);
569
570 4
            $queryParts[] =  'RENAME COLUMN ' . $oldColumnName->getQuotedName($this) .
571 4
                ' TO ' . $column->getQuotedName($this);
572
        }
573
574 24
        $tableSql = [];
575
576 24
        if ( ! $this->onSchemaAlterTable($diff, $tableSql)) {
577 24
            if (count($queryParts) > 0) {
578 18
                $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(" ", $queryParts);
579
            }
580
581
            // Some table alteration operations require a table reorganization.
582 24
            if ( ! empty($diff->removedColumns) || ! empty($diff->changedColumns)) {
583 17
                $sql[] = "CALL SYSPROC.ADMIN_CMD ('REORG TABLE " . $diff->getName($this)->getQuotedName($this) . "')";
584
            }
585
586 24
            $sql = array_merge($sql, $commentsSQL);
587
588 24
            if ($diff->newName !== false) {
589 2
                $sql[] =  'RENAME TABLE ' . $diff->getName($this)->getQuotedName($this) . ' TO ' . $diff->getNewName()->getQuotedName($this);
590
            }
591
592 24
            $sql = array_merge(
593 24
                $this->getPreAlterTableIndexForeignKeySQL($diff),
594 24
                $sql,
595 24
                $this->getPostAlterTableIndexForeignKeySQL($diff)
596
            );
597
        }
598
599 24
        return array_merge($sql, $tableSql, $columnSql);
600
    }
601
602
    /**
603
     * Gathers the table alteration SQL for a given column diff.
604
     *
605
     * @param Table      $table      The table to gather the SQL for.
606
     * @param ColumnDiff $columnDiff The column diff to evaluate.
607
     * @param array      $sql        The sequence of table alteration statements to fill.
608
     * @param array      $queryParts The sequence of column alteration clauses to fill.
609
     */
610 16
    private function gatherAlterColumnSQL(Table $table, ColumnDiff $columnDiff, array &$sql, array &$queryParts)
611
    {
612 16
        $alterColumnClauses = $this->getAlterColumnClausesSQL($columnDiff);
613
614 16
        if (empty($alterColumnClauses)) {
615 2
            return;
616
        }
617
618
        // If we have a single column alteration, we can append the clause to the main query.
619 14
        if (count($alterColumnClauses) === 1) {
620 13
            $queryParts[] = current($alterColumnClauses);
621
622 13
            return;
623
        }
624
625
        // We have multiple alterations for the same column,
626
        // so we need to trigger a complete ALTER TABLE statement
627
        // for each ALTER COLUMN clause.
628 1
        foreach ($alterColumnClauses as $alterColumnClause) {
629 1
            $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ' . $alterColumnClause;
630
        }
631 1
    }
632
633
    /**
634
     * Returns the ALTER COLUMN SQL clauses for altering a column described by the given column diff.
635
     *
636
     * @param ColumnDiff $columnDiff The column diff to evaluate.
637
     *
638
     * @return array
639
     */
640 16
    private function getAlterColumnClausesSQL(ColumnDiff $columnDiff)
641
    {
642 16
        $column = $columnDiff->column->toArray();
643
644 16
        $alterClause = 'ALTER COLUMN ' . $columnDiff->column->getQuotedName($this);
645
646 16
        if ($column['columnDefinition']) {
647 1
            return [$alterClause . ' ' . $column['columnDefinition']];
648
        }
649
650 15
        $clauses = [];
651
652 15
        if ($columnDiff->hasChanged('type') ||
653 12
            $columnDiff->hasChanged('length') ||
654 11
            $columnDiff->hasChanged('precision') ||
655 10
            $columnDiff->hasChanged('scale') ||
656 15
            $columnDiff->hasChanged('fixed')
657
        ) {
658 8
            $clauses[] = $alterClause . ' SET DATA TYPE ' . $column['type']->getSQLDeclaration($column, $this);
659
        }
660
661 15
        if ($columnDiff->hasChanged('notnull')) {
662 4
            $clauses[] = $column['notnull'] ? $alterClause . ' SET NOT NULL' : $alterClause . ' DROP NOT NULL';
663
        }
664
665 15
        if ($columnDiff->hasChanged('default')) {
666 4
            if (isset($column['default'])) {
667 3
                $defaultClause = $this->getDefaultValueDeclarationSQL($column);
668
669 3
                if ($defaultClause) {
670 3
                    $clauses[] = $alterClause . ' SET' . $defaultClause;
671
                }
672
            } else {
673 1
                $clauses[] = $alterClause . ' DROP DEFAULT';
674
            }
675
        }
676
677 15
        return $clauses;
678
    }
679
680
    /**
681
     * {@inheritDoc}
682
     */
683 24
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
684
    {
685 24
        $sql = [];
686 24
        $table = $diff->getName($this)->getQuotedName($this);
687
688 24
        foreach ($diff->removedIndexes as $remKey => $remIndex) {
689
            foreach ($diff->addedIndexes as $addKey => $addIndex) {
690
                if ($remIndex->getColumns() == $addIndex->getColumns()) {
691
                    if ($remIndex->isPrimary()) {
692
                        $sql[] = 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY';
693
                    } elseif ($remIndex->isUnique()) {
694
                        $sql[] = 'ALTER TABLE ' . $table . ' DROP UNIQUE ' . $remIndex->getQuotedName($this);
695
                    } else {
696
                        $sql[] = $this->getDropIndexSQL($remIndex, $table);
697
                    }
698
699
                    $sql[] = $this->getCreateIndexSQL($addIndex, $table);
700
701
                    unset($diff->removedIndexes[$remKey]);
702
                    unset($diff->addedIndexes[$addKey]);
703
704
                    break;
705
                }
706
            }
707
        }
708
709 24
        $sql = array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff));
710
711 24
        return $sql;
712
    }
713
714
    /**
715
     * {@inheritdoc}
716
     */
717 5 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...
718
    {
719 5
        if (strpos($tableName, '.') !== false) {
720 2
            list($schema) = explode('.', $tableName);
721 2
            $oldIndexName = $schema . '.' . $oldIndexName;
722
        }
723
724 5
        return ['RENAME INDEX ' . $oldIndexName . ' TO ' . $index->getQuotedName($this)];
725
    }
726
727
    /**
728
     * {@inheritDoc}
729
     */
730 22
    public function getDefaultValueDeclarationSQL($field)
731
    {
732 22
        if ( ! empty($field['autoincrement'])) {
733 2
            return '';
734
        }
735
736 21
        if (isset($field['version']) && $field['version']) {
737
            if ((string) $field['type'] != "DateTime") {
738
                $field['default'] = "1";
739
            }
740
        }
741
742 21
        return parent::getDefaultValueDeclarationSQL($field);
743
    }
744
745
    /**
746
     * {@inheritDoc}
747
     */
748
    public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName)
749
    {
750
        return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (DEFAULT)';
751
    }
752
753
    /**
754
     * {@inheritDoc}
755
     */
756 1
    public function getCreateTemporaryTableSnippetSQL()
757
    {
758 1
        return "DECLARE GLOBAL TEMPORARY TABLE";
759
    }
760
761
    /**
762
     * {@inheritDoc}
763
     */
764
    public function getTemporaryTableName($tableName)
765
    {
766
        return "SESSION." . $tableName;
767
    }
768
769
    /**
770
     * {@inheritDoc}
771
     */
772 1
    protected function doModifyLimitQuery($query, $limit, $offset = null)
773
    {
774 1
        $where = [];
775
776 1
        if ($offset > 0) {
777 1
            $where[] = sprintf('db22.DC_ROWNUM >= %d', $offset + 1);
778
        }
779
780 1
        if ($limit !== null) {
781 1
            $where[] = sprintf('db22.DC_ROWNUM <= %d', $offset + $limit);
782
        }
783
784 1
        if (empty($where)) {
785 1
            return $query;
786
        }
787
788
        // Todo OVER() needs ORDER BY data!
789 1
        return sprintf(
790 1
            'SELECT db22.* FROM (SELECT db21.*, ROW_NUMBER() OVER() AS DC_ROWNUM FROM (%s) db21) db22 WHERE %s',
791 1
            $query,
792 1
            implode(' AND ', $where)
793
        );
794
    }
795
796
    /**
797
     * {@inheritDoc}
798
     */
799 1 View Code Duplication
    public function getLocateExpression($str, $substr, $startPos = false)
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...
800
    {
801 1
        if ($startPos == false) {
802 1
            return 'LOCATE(' . $substr . ', ' . $str . ')';
803
        }
804
805 1
        return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')';
806
    }
807
808
    /**
809
     * {@inheritDoc}
810
     */
811 1 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...
812
    {
813 1
        if ($length === null) {
814 1
            return 'SUBSTR(' . $value . ', ' . $from . ')';
815
        }
816
817 1
        return 'SUBSTR(' . $value . ', ' . $from . ', ' . $length . ')';
818
    }
819
820
    /**
821
     * {@inheritDoc}
822
     */
823 1
    public function supportsIdentityColumns()
824
    {
825 1
        return true;
826
    }
827
828
    /**
829
     * {@inheritDoc}
830
     */
831 1
    public function prefersIdentityColumns()
832
    {
833 1
        return true;
834
    }
835
836
    /**
837
     * {@inheritDoc}
838
     *
839
     * DB2 returns all column names in SQL result sets in uppercase.
840
     */
841 1
    public function getSQLResultCasing($column)
842
    {
843 1
        return strtoupper($column);
844
    }
845
846
    /**
847
     * {@inheritDoc}
848
     */
849 1
    public function getForUpdateSQL()
850
    {
851 1
        return ' WITH RR USE AND KEEP UPDATE LOCKS';
852
    }
853
854
    /**
855
     * {@inheritDoc}
856
     */
857
    public function getDummySelectSQL()
858
    {
859
        return 'SELECT 1 FROM sysibm.sysdummy1';
860
    }
861
862
    /**
863
     * {@inheritDoc}
864
     *
865
     * DB2 supports savepoints, but they work semantically different than on other vendor platforms.
866
     *
867
     * TODO: We have to investigate how to get DB2 up and running with savepoints.
868
     */
869 1
    public function supportsSavepoints()
870
    {
871 1
        return false;
872
    }
873
874
    /**
875
     * {@inheritDoc}
876
     */
877 52
    protected function getReservedKeywordsClass()
878
    {
879 52
        return Keywords\DB2Keywords::class;
880
    }
881
}
882