Failed Conditions
Push — exceptions ( 7b5f2f...d1ce0d )
by Michael
24:32
created

DB2Platform   F

Complexity

Total Complexity 123

Size/Duplication

Total Lines 872
Duplicated Lines 0 %

Test Coverage

Coverage 89.96%

Importance

Changes 0
Metric Value
wmc 123
dl 0
loc 872
ccs 242
cts 269
cp 0.8996
rs 1.263
c 0
b 0
f 0

60 Methods

Rating   Name   Duplication   Size   Complexity  
A getDropViewSQL() 0 3 1
A isCommentedDoctrineType() 0 9 2
A getIndexDeclarationSQL() 0 4 1
A prefersIdentityColumns() 0 3 1
A _getCreateTableSQL() 0 14 3
A getBinaryTypeDeclarationSQLSnippet() 0 3 1
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 5 2
A getTimeTypeDeclarationSQL() 0 3 1
A getEmptyIdentityInsertSQL() 0 3 1
A getSmallIntTypeDeclarationSQL() 0 3 1
B getListTableForeignKeysSQL() 0 28 1
A getDateTypeDeclarationSQL() 0 3 1
A getCurrentDateSQL() 0 3 1
A getDropDatabaseSQL() 0 3 1
A getLocateExpression() 0 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 28 6
A supportsCommentOnStatement() 0 3 1
A getBitAndComparisonExpression() 0 3 1
F getAlterTableSQL() 0 101 21
A getListTableIndexesSQL() 0 18 1
A getCreateViewSQL() 0 3 1
A gatherAlterColumnSQL() 0 20 4
A getDateArithmeticIntervalExpression() 0 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 getVarcharTypeDeclarationSQL() 0 8 3
A getSubstringExpression() 0 7 2
A getRenameIndexSQL() 0 8 2
A getCharMaxLength() 0 3 1
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

How to fix   Complexity   

Complex Class

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
namespace Doctrine\DBAL\Platforms;
4
5
use Doctrine\DBAL\DBALException;
6
use Doctrine\DBAL\Platforms\Exception\NotSupported;
7
use Doctrine\DBAL\Schema\ColumnDiff;
8
use Doctrine\DBAL\Schema\Identifier;
9
use Doctrine\DBAL\Schema\Index;
10
use Doctrine\DBAL\Schema\Table;
11
use Doctrine\DBAL\Schema\TableDiff;
12
use Doctrine\DBAL\Types\Type;
13
use function array_merge;
14
use function count;
15
use function current;
16
use function explode;
17
use function func_get_arg;
18
use function func_num_args;
19
use function implode;
20
use function sprintf;
21
use function strpos;
22
use function strtoupper;
23
24
class DB2Platform extends AbstractPlatform
25
{
26
    /**
27
     * {@inheritdoc}
28
     */
29 64
    public function getCharMaxLength() : int
30
    {
31 64
        return 254;
32
    }
33
34
    /**
35
     * {@inheritdoc}
36
     */
37 32
    public function getBinaryMaxLength()
38
    {
39 32
        return 32704;
40
    }
41
42
    /**
43
     * {@inheritdoc}
44
     */
45 32
    public function getBinaryDefaultLength()
46
    {
47 32
        return 1;
48
    }
49
50
    /**
51
     * {@inheritDoc}
52
     */
53 240
    public function getVarcharTypeDeclarationSQL(array $field)
54
    {
55
        // for IBM DB2, the CHAR max length is less than VARCHAR default length
56 240
        if (! isset($field['length']) && ! empty($field['fixed'])) {
57 16
            $field['length'] = $this->getCharMaxLength();
58
        }
59
60 240
        return parent::getVarcharTypeDeclarationSQL($field);
61
    }
62
63
    /**
64
     * {@inheritDoc}
65
     */
66 32
    public function getBlobTypeDeclarationSQL(array $field)
67
    {
68
        // todo blob(n) with $field['length'];
69 32
        return 'BLOB(1M)';
70
    }
71
72
    /**
73
     * {@inheritDoc}
74
     */
75 80
    public function initializeDoctrineTypeMappings()
76
    {
77 80
        $this->doctrineTypeMapping = [
78
            'bigint'    => 'bigint',
79
            'binary'    => 'binary',
80
            'blob'      => 'blob',
81
            'character' => 'string',
82
            'clob'      => 'text',
83
            'date'      => 'date',
84
            'decimal'   => 'decimal',
85
            'double'    => 'float',
86
            'integer'   => 'integer',
87
            'real'      => 'float',
88
            'smallint'  => 'smallint',
89
            'time'      => 'time',
90
            'timestamp' => 'datetime',
91
            'varbinary' => 'binary',
92
            'varchar'   => 'string',
93
        ];
94 80
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99 672
    public function isCommentedDoctrineType(Type $doctrineType)
100
    {
101 672
        if ($doctrineType->getName() === Type::BOOLEAN) {
102
            // We require a commented boolean type in order to distinguish between boolean and smallint
103
            // as both (have to) map to the same native type.
104 16
            return true;
105
        }
106
107 656
        return parent::isCommentedDoctrineType($doctrineType);
108
    }
109
110
    /**
111
     * {@inheritDoc}
112
     */
113 256
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
114
    {
115 256
        return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(254)')
116 256
                : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)');
117
    }
118
119
    /**
120
     * {@inheritdoc}
121
     */
122 16
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
123
    {
124 16
        return $this->getVarcharTypeDeclarationSQLSnippet($length, $fixed) . ' FOR BIT DATA';
125
    }
126
127
    /**
128
     * {@inheritDoc}
129
     */
130 48
    public function getClobTypeDeclarationSQL(array $field)
131
    {
132
        // todo clob(n) with $field['length'];
133 48
        return 'CLOB(1M)';
134
    }
135
136
    /**
137
     * {@inheritDoc}
138
     */
139 48
    public function getName()
140
    {
141 48
        return 'db2';
142
    }
143
144
    /**
145
     * {@inheritDoc}
146
     */
147 32
    public function getBooleanTypeDeclarationSQL(array $columnDef)
148
    {
149 32
        return 'SMALLINT';
150
    }
151
152
    /**
153
     * {@inheritDoc}
154
     */
155 192
    public function getIntegerTypeDeclarationSQL(array $columnDef)
156
    {
157 192
        return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
158
    }
159
160
    /**
161
     * {@inheritDoc}
162
     */
163 16
    public function getBigIntTypeDeclarationSQL(array $columnDef)
164
    {
165 16
        return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
166
    }
167
168
    /**
169
     * {@inheritDoc}
170
     */
171 16
    public function getSmallIntTypeDeclarationSQL(array $columnDef)
172
    {
173 16
        return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
174
    }
175
176
    /**
177
     * {@inheritDoc}
178
     */
179 192
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
180
    {
181 192
        $autoinc = '';
182 192
        if ( ! empty($columnDef['autoincrement'])) {
183 32
            $autoinc = ' GENERATED BY DEFAULT AS IDENTITY';
184
        }
185
186 192
        return $autoinc;
187
    }
188
189
    /**
190
     * {@inheritdoc}
191
     */
192 16
    public function getBitAndComparisonExpression($value1, $value2)
193
    {
194 16
        return 'BITAND(' . $value1 . ', ' . $value2 . ')';
195
    }
196
197
    /**
198
     * {@inheritdoc}
199
     */
200 16
    public function getBitOrComparisonExpression($value1, $value2)
201
    {
202 16
        return 'BITOR(' . $value1 . ', ' . $value2 . ')';
203
    }
204
205
    /**
206
     * {@inheritdoc}
207
     */
208 16
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
209
    {
210 15
        switch ($unit) {
211 1
            case DateIntervalUnit::WEEK:
212 16
                $interval *= 7;
213 16
                $unit      = DateIntervalUnit::DAY;
214 16
                break;
215
216 1
            case DateIntervalUnit::QUARTER:
217 16
                $interval *= 3;
218 16
                $unit      = DateIntervalUnit::MONTH;
219 16
                break;
220
        }
221
222 16
        return $date . ' ' . $operator . ' ' . $interval . ' ' . $unit;
223
    }
224
225
    /**
226
     * {@inheritdoc}
227
     */
228 16
    public function getDateDiffExpression($date1, $date2)
229
    {
230 16
        return 'DAYS(' . $date1 . ') - DAYS(' . $date2 . ')';
231
    }
232
233
    /**
234
     * {@inheritDoc}
235
     */
236 16
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
237
    {
238 16
        if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) {
239 16
            return "TIMESTAMP(0) WITH DEFAULT";
240
        }
241
242 16
        return 'TIMESTAMP(0)';
243
    }
244
245
    /**
246
     * {@inheritDoc}
247
     */
248 16
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
249
    {
250 16
        return 'DATE';
251
    }
252
253
    /**
254
     * {@inheritDoc}
255
     */
256 16
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
257
    {
258 16
        return 'TIME';
259
    }
260
261
    /**
262
     * {@inheritdoc}
263
     */
264 32
    public function getTruncateTableSQL($tableName, $cascade = false)
265
    {
266 32
        $tableIdentifier = new Identifier($tableName);
267
268 32
        return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this) . ' IMMEDIATE';
269
    }
270
271
    /**
272
     * This code fragment is originally from the Zend_Db_Adapter_Db2 class, but has been edited.
273
     *
274
     * @license New BSD License
275
     *
276
     * @param string $table
277
     * @param string $database
278
     *
279
     * @return string
280
     */
281 16
    public function getListTableColumnsSQL($table, $database = null)
282
    {
283 16
        $table = $this->quoteStringLiteral($table);
284
285
        // We do the funky subquery and join syscat.columns.default this crazy way because
286
        // as of db2 v10, the column is CLOB(64k) and the distinct operator won't allow a CLOB,
287
        // it wants shorter stuff like a varchar.
288
        return "
289
        SELECT
290
          cols.default,
291
          subq.*
292
        FROM (
293
               SELECT DISTINCT
294
                 c.tabschema,
295
                 c.tabname,
296
                 c.colname,
297
                 c.colno,
298
                 c.typename,
299
                 c.nulls,
300
                 c.length,
301
                 c.scale,
302
                 c.identity,
303
                 tc.type AS tabconsttype,
304
                 c.remarks AS comment,
305
                 k.colseq,
306
                 CASE
307
                 WHEN c.generated = 'D' THEN 1
308
                 ELSE 0
309
                 END     AS autoincrement
310
               FROM syscat.columns c
311
                 LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc
312
                     ON (k.tabschema = tc.tabschema
313
                         AND k.tabname = tc.tabname
314
                         AND tc.type = 'P'))
315
                   ON (c.tabschema = k.tabschema
316
                       AND c.tabname = k.tabname
317
                       AND c.colname = k.colname)
318 16
               WHERE UPPER(c.tabname) = UPPER(" . $table . ")
319
               ORDER BY c.colno
320
             ) subq
321
          JOIN syscat.columns cols
322
            ON subq.tabschema = cols.tabschema
323
               AND subq.tabname = cols.tabname
324
               AND subq.colno = cols.colno
325
        ORDER BY subq.colno
326
        ";
327
    }
328
329
    /**
330
     * {@inheritDoc}
331
     */
332
    public function getListTablesSQL()
333
    {
334
        return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = 'T'";
335
    }
336
337
    /**
338
     * {@inheritDoc}
339
     */
340
    public function getListViewsSQL($database)
341
    {
342
        return "SELECT NAME, TEXT FROM SYSIBM.SYSVIEWS";
343
    }
344
345
    /**
346
     * {@inheritDoc}
347
     */
348 16
    public function getListTableIndexesSQL($table, $currentDatabase = null)
349
    {
350 16
        $table = $this->quoteStringLiteral($table);
351
352
        return "SELECT   idx.INDNAME AS key_name,
353
                         idxcol.COLNAME AS column_name,
354
                         CASE
355
                             WHEN idx.UNIQUERULE = 'P' THEN 1
356
                             ELSE 0
357
                         END AS primary,
358
                         CASE
359
                             WHEN idx.UNIQUERULE = 'D' THEN 1
360
                             ELSE 0
361
                         END AS non_unique
362
                FROM     SYSCAT.INDEXES AS idx
363
                JOIN     SYSCAT.INDEXCOLUSE AS idxcol
364
                ON       idx.INDSCHEMA = idxcol.INDSCHEMA AND idx.INDNAME = idxcol.INDNAME
365 16
                WHERE    idx.TABNAME = UPPER(" . $table . ")
366
                ORDER BY idxcol.COLSEQ ASC";
367
    }
368
369
    /**
370
     * {@inheritDoc}
371
     */
372 16
    public function getListTableForeignKeysSQL($table)
373
    {
374 16
        $table = $this->quoteStringLiteral($table);
375
376
        return "SELECT   fkcol.COLNAME AS local_column,
377
                         fk.REFTABNAME AS foreign_table,
378
                         pkcol.COLNAME AS foreign_column,
379
                         fk.CONSTNAME AS index_name,
380
                         CASE
381
                             WHEN fk.UPDATERULE = 'R' THEN 'RESTRICT'
382
                             ELSE NULL
383
                         END AS on_update,
384
                         CASE
385
                             WHEN fk.DELETERULE = 'C' THEN 'CASCADE'
386
                             WHEN fk.DELETERULE = 'N' THEN 'SET NULL'
387
                             WHEN fk.DELETERULE = 'R' THEN 'RESTRICT'
388
                             ELSE NULL
389
                         END AS on_delete
390
                FROM     SYSCAT.REFERENCES AS fk
391
                JOIN     SYSCAT.KEYCOLUSE AS fkcol
392
                ON       fk.CONSTNAME = fkcol.CONSTNAME
393
                AND      fk.TABSCHEMA = fkcol.TABSCHEMA
394
                AND      fk.TABNAME = fkcol.TABNAME
395
                JOIN     SYSCAT.KEYCOLUSE AS pkcol
396
                ON       fk.REFKEYNAME = pkcol.CONSTNAME
397
                AND      fk.REFTABSCHEMA = pkcol.TABSCHEMA
398
                AND      fk.REFTABNAME = pkcol.TABNAME
399 16
                WHERE    fk.TABNAME = UPPER(" . $table . ")
400
                ORDER BY fkcol.COLSEQ ASC";
401
    }
402
403
    /**
404
     * {@inheritDoc}
405
     */
406 16
    public function getCreateViewSQL($name, $sql)
407
    {
408 16
        return "CREATE VIEW ".$name." AS ".$sql;
409
    }
410
411
    /**
412
     * {@inheritDoc}
413
     */
414 16
    public function getDropViewSQL($name)
415
    {
416 16
        return "DROP VIEW ".$name;
417
    }
418
419
    /**
420
     * {@inheritDoc}
421
     */
422 16
    public function getCreateDatabaseSQL($database)
423
    {
424 16
        return "CREATE DATABASE ".$database;
425
    }
426
427
    /**
428
     * {@inheritDoc}
429
     */
430 16
    public function getDropDatabaseSQL($database)
431
    {
432 16
        return "DROP DATABASE " . $database;
433
    }
434
435
    /**
436
     * {@inheritDoc}
437
     */
438 16
    public function supportsCreateDropDatabase()
439
    {
440 16
        return false;
441
    }
442
443
    /**
444
     * {@inheritDoc}
445
     */
446 16
    public function supportsReleaseSavepoints()
447
    {
448 16
        return false;
449
    }
450
451
    /**
452
     * {@inheritdoc}
453
     */
454 208
    public function supportsCommentOnStatement()
455
    {
456 208
        return true;
457
    }
458
459
    /**
460
     * {@inheritDoc}
461
     */
462 32
    public function getCurrentDateSQL()
463
    {
464 32
        return 'CURRENT DATE';
465
    }
466
467
    /**
468
     * {@inheritDoc}
469
     */
470 16
    public function getCurrentTimeSQL()
471
    {
472 16
        return 'CURRENT TIME';
473
    }
474
475
    /**
476
     * {@inheritDoc}
477
     */
478 32
    public function getCurrentTimestampSQL()
479
    {
480 32
        return "CURRENT TIMESTAMP";
481
    }
482
483
    /**
484
     * {@inheritDoc}
485
     */
486 16
    public function getIndexDeclarationSQL($name, Index $index)
487
    {
488
        // Index declaration in statements like CREATE TABLE is not supported.
489 16
        throw NotSupported::new(__METHOD__);
490
    }
491
492
    /**
493
     * {@inheritDoc}
494
     */
495 192
    protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
496
    {
497 192
        $indexes = [];
498 192
        if (isset($options['indexes'])) {
499 192
            $indexes = $options['indexes'];
500
        }
501 192
        $options['indexes'] = [];
502
503 192
        $sqls = parent::_getCreateTableSQL($tableName, $columns, $options);
504
505 192
        foreach ($indexes as $definition) {
506 64
            $sqls[] = $this->getCreateIndexSQL($definition, $tableName);
507
        }
508 192
        return $sqls;
509
    }
510
511
    /**
512
     * {@inheritDoc}
513
     */
514 384
    public function getAlterTableSQL(TableDiff $diff)
515
    {
516 384
        $sql = [];
517 384
        $columnSql = [];
518 384
        $commentsSQL = [];
519
520 384
        $queryParts = [];
521 384
        foreach ($diff->addedColumns as $column) {
522 64
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
523
                continue;
524
            }
525
526 64
            $columnDef = $column->toArray();
527 64
            $queryPart = 'ADD COLUMN ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef);
528
529
            // Adding non-nullable columns to a table requires a default value to be specified.
530 64
            if ( ! empty($columnDef['notnull']) &&
531 64
                ! isset($columnDef['default']) &&
532 64
                empty($columnDef['autoincrement'])
533
            ) {
534 48
                $queryPart .= ' WITH DEFAULT';
535
            }
536
537 64
            $queryParts[] = $queryPart;
538
539 64
            $comment = $this->getColumnComment($column);
540
541 64
            if (null !== $comment && '' !== $comment) {
542 16
                $commentsSQL[] = $this->getCommentOnColumnSQL(
543 16
                    $diff->getName($this)->getQuotedName($this),
544 16
                    $column->getQuotedName($this),
545 64
                    $comment
546
                );
547
            }
548
        }
549
550 384
        foreach ($diff->removedColumns as $column) {
551 48
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
552
                continue;
553
            }
554
555 48
            $queryParts[] =  'DROP COLUMN ' . $column->getQuotedName($this);
556
        }
557
558 384
        foreach ($diff->changedColumns as $columnDiff) {
559 272
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
560
                continue;
561
            }
562
563 272
            if ($columnDiff->hasChanged('comment')) {
564 16
                $commentsSQL[] = $this->getCommentOnColumnSQL(
565 16
                    $diff->getName($this)->getQuotedName($this),
566 16
                    $columnDiff->column->getQuotedName($this),
567 16
                    $this->getColumnComment($columnDiff->column)
568
                );
569
570 16
                if (count($columnDiff->changedProperties) === 1) {
571 16
                    continue;
572
                }
573
            }
574
575 256
            $this->gatherAlterColumnSQL($diff->fromTable, $columnDiff, $sql, $queryParts);
576
        }
577
578 384
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
579 64
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
580
                continue;
581
            }
582
583 64
            $oldColumnName = new Identifier($oldColumnName);
584
585 64
            $queryParts[] =  'RENAME COLUMN ' . $oldColumnName->getQuotedName($this) .
586 64
                ' TO ' . $column->getQuotedName($this);
587
        }
588
589 384
        $tableSql = [];
590
591 384
        if ( ! $this->onSchemaAlterTable($diff, $tableSql)) {
592 384
            if (count($queryParts) > 0) {
593 288
                $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(" ", $queryParts);
594
            }
595
596
            // Some table alteration operations require a table reorganization.
597 384
            if ( ! empty($diff->removedColumns) || ! empty($diff->changedColumns)) {
598 272
                $sql[] = "CALL SYSPROC.ADMIN_CMD ('REORG TABLE " . $diff->getName($this)->getQuotedName($this) . "')";
599
            }
600
601 384
            $sql = array_merge($sql, $commentsSQL);
602
603 384
            if ($diff->newName !== false) {
604 32
                $sql[] =  'RENAME TABLE ' . $diff->getName($this)->getQuotedName($this) . ' TO ' . $diff->getNewName()->getQuotedName($this);
605
            }
606
607 384
            $sql = array_merge(
608 384
                $this->getPreAlterTableIndexForeignKeySQL($diff),
609 384
                $sql,
610 384
                $this->getPostAlterTableIndexForeignKeySQL($diff)
611
            );
612
        }
613
614 384
        return array_merge($sql, $tableSql, $columnSql);
615
    }
616
617
    /**
618
     * Gathers the table alteration SQL for a given column diff.
619
     *
620
     * @param Table      $table      The table to gather the SQL for.
621
     * @param ColumnDiff $columnDiff The column diff to evaluate.
622
     * @param array      $sql        The sequence of table alteration statements to fill.
623
     * @param array      $queryParts The sequence of column alteration clauses to fill.
624
     */
625 256
    private function gatherAlterColumnSQL(Table $table, ColumnDiff $columnDiff, array &$sql, array &$queryParts)
626
    {
627 256
        $alterColumnClauses = $this->getAlterColumnClausesSQL($columnDiff);
628
629 256
        if (empty($alterColumnClauses)) {
630 32
            return;
631
        }
632
633
        // If we have a single column alteration, we can append the clause to the main query.
634 224
        if (count($alterColumnClauses) === 1) {
635 208
            $queryParts[] = current($alterColumnClauses);
636
637 208
            return;
638
        }
639
640
        // We have multiple alterations for the same column,
641
        // so we need to trigger a complete ALTER TABLE statement
642
        // for each ALTER COLUMN clause.
643 16
        foreach ($alterColumnClauses as $alterColumnClause) {
644 16
            $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ' . $alterColumnClause;
645
        }
646 16
    }
647
648
    /**
649
     * Returns the ALTER COLUMN SQL clauses for altering a column described by the given column diff.
650
     *
651
     * @param ColumnDiff $columnDiff The column diff to evaluate.
652
     *
653
     * @return array
654
     */
655 256
    private function getAlterColumnClausesSQL(ColumnDiff $columnDiff)
656
    {
657 256
        $column = $columnDiff->column->toArray();
658
659 256
        $alterClause = 'ALTER COLUMN ' . $columnDiff->column->getQuotedName($this);
660
661 256
        if ($column['columnDefinition']) {
662 16
            return [$alterClause . ' ' . $column['columnDefinition']];
663
        }
664
665 240
        $clauses = [];
666
667 240
        if ($columnDiff->hasChanged('type') ||
668 192
            $columnDiff->hasChanged('length') ||
669 176
            $columnDiff->hasChanged('precision') ||
670 160
            $columnDiff->hasChanged('scale') ||
671 240
            $columnDiff->hasChanged('fixed')
672
        ) {
673 128
            $clauses[] = $alterClause . ' SET DATA TYPE ' . $column['type']->getSQLDeclaration($column, $this);
674
        }
675
676 240
        if ($columnDiff->hasChanged('notnull')) {
677 64
            $clauses[] = $column['notnull'] ? $alterClause . ' SET NOT NULL' : $alterClause . ' DROP NOT NULL';
678
        }
679
680 240
        if ($columnDiff->hasChanged('default')) {
681 64
            if (isset($column['default'])) {
682 48
                $defaultClause = $this->getDefaultValueDeclarationSQL($column);
683
684 48
                if ($defaultClause) {
685 48
                    $clauses[] = $alterClause . ' SET' . $defaultClause;
686
                }
687
            } else {
688 16
                $clauses[] = $alterClause . ' DROP DEFAULT';
689
            }
690
        }
691
692 240
        return $clauses;
693
    }
694
695
    /**
696
     * {@inheritDoc}
697
     */
698 384
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
699
    {
700 384
        $sql = [];
701 384
        $table = $diff->getName($this)->getQuotedName($this);
702
703 384
        foreach ($diff->removedIndexes as $remKey => $remIndex) {
704
            foreach ($diff->addedIndexes as $addKey => $addIndex) {
705
                if ($remIndex->getColumns() == $addIndex->getColumns()) {
706
                    if ($remIndex->isPrimary()) {
707
                        $sql[] = 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY';
708
                    } elseif ($remIndex->isUnique()) {
709
                        $sql[] = 'ALTER TABLE ' . $table . ' DROP UNIQUE ' . $remIndex->getQuotedName($this);
710
                    } else {
711
                        $sql[] = $this->getDropIndexSQL($remIndex, $table);
712
                    }
713
714
                    $sql[] = $this->getCreateIndexSQL($addIndex, $table);
715
716
                    unset($diff->removedIndexes[$remKey], $diff->addedIndexes[$addKey]);
717
718
                    break;
719
                }
720
            }
721
        }
722
723 384
        $sql = array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff));
724
725 384
        return $sql;
726
    }
727
728
    /**
729
     * {@inheritdoc}
730
     */
731 80
    protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
732
    {
733 80
        if (strpos($tableName, '.') !== false) {
734 32
            list($schema) = explode('.', $tableName);
735 32
            $oldIndexName = $schema . '.' . $oldIndexName;
736
        }
737
738 80
        return ['RENAME INDEX ' . $oldIndexName . ' TO ' . $index->getQuotedName($this)];
739
    }
740
741
    /**
742
     * {@inheritDoc}
743
     */
744 352
    public function getDefaultValueDeclarationSQL($field)
745
    {
746 352
        if ( ! empty($field['autoincrement'])) {
747 32
            return '';
748
        }
749
750 336
        if (isset($field['version']) && $field['version']) {
751
            if ((string) $field['type'] != "DateTime") {
752
                $field['default'] = "1";
753
            }
754
        }
755
756 336
        return parent::getDefaultValueDeclarationSQL($field);
757
    }
758
759
    /**
760
     * {@inheritDoc}
761
     */
762
    public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName)
763
    {
764
        return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (DEFAULT)';
765
    }
766
767
    /**
768
     * {@inheritDoc}
769
     */
770 16
    public function getCreateTemporaryTableSnippetSQL()
771
    {
772 16
        return "DECLARE GLOBAL TEMPORARY TABLE";
773
    }
774
775
    /**
776
     * {@inheritDoc}
777
     */
778
    public function getTemporaryTableName($tableName)
779
    {
780
        return "SESSION." . $tableName;
781
    }
782
783
    /**
784
     * {@inheritDoc}
785
     */
786 16
    protected function doModifyLimitQuery($query, $limit, $offset = null)
787
    {
788 16
        $where = [];
789
790 16
        if ($offset > 0) {
791 16
            $where[] = sprintf('db22.DC_ROWNUM >= %d', $offset + 1);
792
        }
793
794 16
        if ($limit !== null) {
795 16
            $where[] = sprintf('db22.DC_ROWNUM <= %d', $offset + $limit);
796
        }
797
798 16
        if (empty($where)) {
799 16
            return $query;
800
        }
801
802
        // Todo OVER() needs ORDER BY data!
803 16
        return sprintf(
804 16
            'SELECT db22.* FROM (SELECT db21.*, ROW_NUMBER() OVER() AS DC_ROWNUM FROM (%s) db21) db22 WHERE %s',
805 16
            $query,
806 16
            implode(' AND ', $where)
807
        );
808
    }
809
810
    /**
811
     * {@inheritDoc}
812
     */
813 16
    public function getLocateExpression($str, $substr, $startPos = false)
814
    {
815 16
        if ($startPos == false) {
816 16
            return 'LOCATE(' . $substr . ', ' . $str . ')';
817
        }
818
819 16
        return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')';
820
    }
821
822
    /**
823
     * {@inheritDoc}
824
     */
825 16
    public function getSubstringExpression($value, $from, $length = null)
826
    {
827 16
        if ($length === null) {
828 16
            return 'SUBSTR(' . $value . ', ' . $from . ')';
829
        }
830
831 16
        return 'SUBSTR(' . $value . ', ' . $from . ', ' . $length . ')';
832
    }
833
834
    /**
835
     * {@inheritDoc}
836
     */
837 16
    public function supportsIdentityColumns()
838
    {
839 16
        return true;
840
    }
841
842
    /**
843
     * {@inheritDoc}
844
     */
845 16
    public function prefersIdentityColumns()
846
    {
847 16
        return true;
848
    }
849
850
    /**
851
     * {@inheritDoc}
852
     *
853
     * DB2 returns all column names in SQL result sets in uppercase.
854
     */
855 16
    public function getSQLResultCasing($column)
856
    {
857 16
        return strtoupper($column);
858
    }
859
860
    /**
861
     * {@inheritDoc}
862
     */
863 16
    public function getForUpdateSQL()
864
    {
865 16
        return ' WITH RR USE AND KEEP UPDATE LOCKS';
866
    }
867
868
    /**
869
     * {@inheritDoc}
870
     */
871
    public function getDummySelectSQL()
872
    {
873
        $expression = func_num_args() > 0 ? func_get_arg(0) : '1';
874
875
        return sprintf('SELECT %s FROM sysibm.sysdummy1', $expression);
876
    }
877
878
    /**
879
     * {@inheritDoc}
880
     *
881
     * DB2 supports savepoints, but they work semantically different than on other vendor platforms.
882
     *
883
     * TODO: We have to investigate how to get DB2 up and running with savepoints.
884
     */
885 16
    public function supportsSavepoints()
886
    {
887 16
        return false;
888
    }
889
890
    /**
891
     * {@inheritDoc}
892
     */
893 832
    protected function getReservedKeywordsClass()
894
    {
895 832
        return Keywords\DB2Keywords::class;
896
    }
897
}
898