Failed Conditions
Push — 2.9 ( 741860...9a352c )
by Sergei
72:10 queued 24:00
created

DB2Platform   F

Complexity

Total Complexity 123

Size/Duplication

Total Lines 872
Duplicated Lines 0 %

Test Coverage

Coverage 94.44%

Importance

Changes 0
Metric Value
wmc 123
eloc 228
dl 0
loc 872
ccs 255
cts 270
cp 0.9444
rs 2
c 0
b 0
f 0

60 Methods

Rating   Name   Duplication   Size   Complexity  
A getDropViewSQL() 0 3 1
A getIndexDeclarationSQL() 0 4 1
A getCurrentTimeSQL() 0 3 1
A getCurrentDateSQL() 0 3 1
A getDropDatabaseSQL() 0 3 1
A supportsReleaseSavepoints() 0 3 1
A supportsCommentOnStatement() 0 3 1
A getCreateViewSQL() 0 3 1
A getCurrentTimestampSQL() 0 3 1
A supportsCreateDropDatabase() 0 3 1
A getCreateDatabaseSQL() 0 3 1
A isCommentedDoctrineType() 0 9 2
A prefersIdentityColumns() 0 3 1
A getBinaryTypeDeclarationSQLSnippet() 0 3 1
A getSQLResultCasing() 0 3 1
A 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 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 getTimeTypeDeclarationSQL() 0 3 1
A getSmallIntTypeDeclarationSQL() 0 3 1
A getDateTypeDeclarationSQL() 0 3 1
A getLocateExpression() 0 7 2
A getBinaryDefaultLength() 0 3 1
A getReservedKeywordsClass() 0 3 1
A getCreateTemporaryTableSnippetSQL() 0 3 1
C getAlterColumnClausesSQL() 0 38 12
A getBitAndComparisonExpression() 0 3 1
A gatherAlterColumnSQL() 0 20 4
A getDateArithmeticIntervalExpression() 0 15 3
A getDateTimeTypeDeclarationSQL() 0 7 3
A doModifyLimitQuery() 0 21 4
A getBooleanTypeDeclarationSQL() 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 _getCommonIntegerTypeDeclarationSQL() 0 8 2
A getTruncateTableSQL() 0 5 1
A _getCreateTableSQL() 0 14 3
A getListViewsSQL() 0 3 1
A getDummySelectSQL() 0 5 2
A getEmptyIdentityInsertSQL() 0 3 1
A getListTableForeignKeysSQL() 0 28 1
A getListTableColumnsSQL() 0 38 1
F getAlterTableSQL() 0 103 21
A getListTableIndexesSQL() 0 18 1
A getListTablesSQL() 0 3 1
A getTemporaryTableName() 0 3 1
A getPreAlterTableIndexForeignKeySQL() 0 28 6

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