Failed Conditions
Pull Request — master (#3319)
by Massimiliano
11:33
created

DB2Platform   F

Complexity

Total Complexity 123

Size/Duplication

Total Lines 872
Duplicated Lines 0 %

Test Coverage

Coverage 90%

Importance

Changes 0
Metric Value
wmc 123
eloc 228
dl 0
loc 872
ccs 243
cts 270
cp 0.9
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 getListViewsSQL() 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 getListTablesSQL() 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 getDummySelectSQL() 0 5 2
A getTimeTypeDeclarationSQL() 0 3 1
A getEmptyIdentityInsertSQL() 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 getPreAlterTableIndexForeignKeySQL() 0 28 6
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 getTemporaryTableName() 0 3 1
A getTruncateTableSQL() 0 5 1
A _getCreateTableSQL() 0 14 3
A getListTableForeignKeysSQL() 0 28 1
A getListTableColumnsSQL() 0 38 1
F getAlterTableSQL() 0 103 21
A getListTableIndexesSQL() 0 18 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\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 84
    public function getCharMaxLength() : int
29
    {
30 84
        return 254;
31
    }
32
33
    /**
34
     * {@inheritdoc}
35
     */
36 63
    public function getBinaryMaxLength()
37
    {
38 63
        return 32704;
39
    }
40
41
    /**
42
     * {@inheritdoc}
43
     */
44 42
    public function getBinaryDefaultLength()
45
    {
46 42
        return 1;
47
    }
48
49
    /**
50
     * {@inheritDoc}
51
     */
52 315
    public function getVarcharTypeDeclarationSQL(array $field)
53
    {
54
        // for IBM DB2, the CHAR max length is less than VARCHAR default length
55 315
        if (! isset($field['length']) && ! empty($field['fixed'])) {
56 21
            $field['length'] = $this->getCharMaxLength();
57
        }
58
59 315
        return parent::getVarcharTypeDeclarationSQL($field);
60
    }
61
62
    /**
63
     * {@inheritDoc}
64
     */
65 42
    public function getBlobTypeDeclarationSQL(array $field)
66
    {
67
        // todo blob(n) with $field['length'];
68 42
        return 'BLOB(1M)';
69
    }
70
71
    /**
72
     * {@inheritDoc}
73
     */
74 105
    public function initializeDoctrineTypeMappings()
75
    {
76 105
        $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 105
    }
94
95
    /**
96
     * {@inheritdoc}
97
     */
98 882
    public function isCommentedDoctrineType(Type $doctrineType)
99
    {
100 882
        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 21
            return true;
104
        }
105
106 861
        return parent::isCommentedDoctrineType($doctrineType);
107
    }
108
109
    /**
110
     * {@inheritDoc}
111
     */
112 336
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
113
    {
114 336
        return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(254)')
115 336
                : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)');
116
    }
117
118
    /**
119
     * {@inheritdoc}
120
     */
121 21
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
122
    {
123 21
        return $this->getVarcharTypeDeclarationSQLSnippet($length, $fixed) . ' FOR BIT DATA';
124
    }
125
126
    /**
127
     * {@inheritDoc}
128
     */
129 63
    public function getClobTypeDeclarationSQL(array $field)
130
    {
131
        // todo clob(n) with $field['length'];
132 63
        return 'CLOB(1M)';
133
    }
134
135
    /**
136
     * {@inheritDoc}
137
     */
138 63
    public function getName()
139
    {
140 63
        return 'db2';
141
    }
142
143
    /**
144
     * {@inheritDoc}
145
     */
146 42
    public function getBooleanTypeDeclarationSQL(array $columnDef)
147
    {
148 42
        return 'SMALLINT';
149
    }
150
151
    /**
152
     * {@inheritDoc}
153
     */
154 252
    public function getIntegerTypeDeclarationSQL(array $columnDef)
155
    {
156 252
        return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
157
    }
158
159
    /**
160
     * {@inheritDoc}
161
     */
162 21
    public function getBigIntTypeDeclarationSQL(array $columnDef)
163
    {
164 21
        return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
165
    }
166
167
    /**
168
     * {@inheritDoc}
169
     */
170 21
    public function getSmallIntTypeDeclarationSQL(array $columnDef)
171
    {
172 21
        return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
173
    }
174
175
    /**
176
     * {@inheritDoc}
177
     */
178 252
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
179
    {
180 252
        $autoinc = '';
181 252
        if (! empty($columnDef['autoincrement'])) {
182 42
            $autoinc = ' GENERATED BY DEFAULT AS IDENTITY';
183
        }
184
185 252
        return $autoinc;
186
    }
187
188
    /**
189
     * {@inheritdoc}
190
     */
191 21
    public function getBitAndComparisonExpression($value1, $value2)
192
    {
193 21
        return 'BITAND(' . $value1 . ', ' . $value2 . ')';
194
    }
195
196
    /**
197
     * {@inheritdoc}
198
     */
199 21
    public function getBitOrComparisonExpression($value1, $value2)
200
    {
201 21
        return 'BITOR(' . $value1 . ', ' . $value2 . ')';
202
    }
203
204
    /**
205
     * {@inheritdoc}
206
     */
207 21
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
208
    {
209 2
        switch ($unit) {
210 19
            case DateIntervalUnit::WEEK:
211 21
                $interval *= 7;
212 21
                $unit      = DateIntervalUnit::DAY;
213 21
                break;
214
215 19
            case DateIntervalUnit::QUARTER:
216 21
                $interval *= 3;
217 21
                $unit      = DateIntervalUnit::MONTH;
218 21
                break;
219
        }
220
221 21
        return $date . ' ' . $operator . ' ' . $interval . ' ' . $unit;
222
    }
223
224
    /**
225
     * {@inheritdoc}
226
     */
227 21
    public function getDateDiffExpression($date1, $date2)
228
    {
229 21
        return 'DAYS(' . $date1 . ') - DAYS(' . $date2 . ')';
230
    }
231
232
    /**
233
     * {@inheritDoc}
234
     */
235 21
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
236
    {
237 21
        if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] === true) {
238 21
            return 'TIMESTAMP(0) WITH DEFAULT';
239
        }
240
241 21
        return 'TIMESTAMP(0)';
242
    }
243
244
    /**
245
     * {@inheritDoc}
246
     */
247 21
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
248
    {
249 21
        return 'DATE';
250
    }
251
252
    /**
253
     * {@inheritDoc}
254
     */
255 21
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
256
    {
257 21
        return 'TIME';
258
    }
259
260
    /**
261
     * {@inheritdoc}
262
     */
263 42
    public function getTruncateTableSQL($tableName, $cascade = false)
264
    {
265 42
        $tableIdentifier = new Identifier($tableName);
266
267 42
        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 21
    public function getListTableColumnsSQL($table, $database = null)
279
    {
280 21
        $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 21
               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
    public function getListTablesSQL()
330
    {
331
        return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = 'T'";
332
    }
333
334
    /**
335
     * {@inheritDoc}
336
     */
337
    public function getListViewsSQL($database)
338
    {
339
        return 'SELECT NAME, TEXT FROM SYSIBM.SYSVIEWS';
340
    }
341
342
    /**
343
     * {@inheritDoc}
344
     */
345 21
    public function getListTableIndexesSQL($table, $currentDatabase = null)
346
    {
347 21
        $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 21
                WHERE    idx.TABNAME = UPPER(" . $table . ')
363
                ORDER BY idxcol.COLSEQ ASC';
364
    }
365
366
    /**
367
     * {@inheritDoc}
368
     */
369 21
    public function getListTableForeignKeysSQL($table)
370
    {
371 21
        $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 21
                WHERE    fk.TABNAME = UPPER(" . $table . ')
397
                ORDER BY fkcol.COLSEQ ASC';
398
    }
399
400
    /**
401
     * {@inheritDoc}
402
     */
403 21
    public function getCreateViewSQL($name, $sql)
404
    {
405 21
        return 'CREATE VIEW ' . $name . ' AS ' . $sql;
406
    }
407
408
    /**
409
     * {@inheritDoc}
410
     */
411 21
    public function getDropViewSQL($name)
412
    {
413 21
        return 'DROP VIEW ' . $name;
414
    }
415
416
    /**
417
     * {@inheritDoc}
418
     */
419 21
    public function getCreateDatabaseSQL($database)
420
    {
421 21
        return 'CREATE DATABASE ' . $database;
422
    }
423
424
    /**
425
     * {@inheritDoc}
426
     */
427 21
    public function getDropDatabaseSQL($database)
428
    {
429 21
        return 'DROP DATABASE ' . $database;
430
    }
431
432
    /**
433
     * {@inheritDoc}
434
     */
435 21
    public function supportsCreateDropDatabase()
436
    {
437 21
        return false;
438
    }
439
440
    /**
441
     * {@inheritDoc}
442
     */
443 21
    public function supportsReleaseSavepoints()
444
    {
445 21
        return false;
446
    }
447
448
    /**
449
     * {@inheritdoc}
450
     */
451 273
    public function supportsCommentOnStatement()
452
    {
453 273
        return true;
454
    }
455
456
    /**
457
     * {@inheritDoc}
458
     */
459 42
    public function getCurrentDateSQL()
460
    {
461 42
        return 'CURRENT DATE';
462
    }
463
464
    /**
465
     * {@inheritDoc}
466
     */
467 21
    public function getCurrentTimeSQL()
468
    {
469 21
        return 'CURRENT TIME';
470
    }
471
472
    /**
473
     * {@inheritDoc}
474
     */
475 42
    public function getCurrentTimestampSQL()
476
    {
477 42
        return 'CURRENT TIMESTAMP';
478
    }
479
480
    /**
481
     * {@inheritDoc}
482
     */
483 21
    public function getIndexDeclarationSQL($name, Index $index)
484
    {
485
        // Index declaration in statements like CREATE TABLE is not supported.
486 21
        throw DBALException::notSupported(__METHOD__);
487
    }
488
489
    /**
490
     * {@inheritDoc}
491
     */
492 252
    protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
493
    {
494 252
        $indexes = [];
495 252
        if (isset($options['indexes'])) {
496 252
            $indexes = $options['indexes'];
497
        }
498 252
        $options['indexes'] = [];
499
500 252
        $sqls = parent::_getCreateTableSQL($tableName, $columns, $options);
501
502 252
        foreach ($indexes as $definition) {
503 84
            $sqls[] = $this->getCreateIndexSQL($definition, $tableName);
504
        }
505 252
        return $sqls;
506
    }
507
508
    /**
509
     * {@inheritDoc}
510
     */
511 504
    public function getAlterTableSQL(TableDiff $diff)
512
    {
513 504
        $sql         = [];
514 504
        $columnSql   = [];
515 504
        $commentsSQL = [];
516
517 504
        $queryParts = [];
518 504
        foreach ($diff->addedColumns as $column) {
519 84
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
520
                continue;
521
            }
522
523 84
            $columnDef = $column->toArray();
524 84
            $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 84
            if (! empty($columnDef['notnull']) &&
528 84
                ! isset($columnDef['default']) &&
529 84
                empty($columnDef['autoincrement'])
530
            ) {
531 63
                $queryPart .= ' WITH DEFAULT';
532
            }
533
534 84
            $queryParts[] = $queryPart;
535
536 84
            $comment = $this->getColumnComment($column);
537
538 84
            if ($comment === null || $comment === '') {
539 63
                continue;
540
            }
541
542 21
            $commentsSQL[] = $this->getCommentOnColumnSQL(
543 21
                $diff->getName($this)->getQuotedName($this),
544 21
                $column->getQuotedName($this),
545 21
                $comment
546
            );
547
        }
548
549 504
        foreach ($diff->removedColumns as $column) {
550 63
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
551
                continue;
552
            }
553
554 63
            $queryParts[] =  'DROP COLUMN ' . $column->getQuotedName($this);
555
        }
556
557 504
        foreach ($diff->changedColumns as $columnDiff) {
558 357
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
559
                continue;
560
            }
561
562 357
            if ($columnDiff->hasChanged('comment')) {
563 21
                $commentsSQL[] = $this->getCommentOnColumnSQL(
564 21
                    $diff->getName($this)->getQuotedName($this),
565 21
                    $columnDiff->column->getQuotedName($this),
566 21
                    $this->getColumnComment($columnDiff->column)
567
                );
568
569 21
                if (count($columnDiff->changedProperties) === 1) {
570 21
                    continue;
571
                }
572
            }
573
574 336
            $this->gatherAlterColumnSQL($diff->fromTable, $columnDiff, $sql, $queryParts);
575
        }
576
577 504
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
578 84
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
579
                continue;
580
            }
581
582 84
            $oldColumnName = new Identifier($oldColumnName);
583
584 84
            $queryParts[] =  'RENAME COLUMN ' . $oldColumnName->getQuotedName($this) .
585 84
                ' TO ' . $column->getQuotedName($this);
586
        }
587
588 504
        $tableSql = [];
589
590 504
        if (! $this->onSchemaAlterTable($diff, $tableSql)) {
591 504
            if (count($queryParts) > 0) {
592 378
                $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(' ', $queryParts);
593
            }
594
595
            // Some table alteration operations require a table reorganization.
596 504
            if (! empty($diff->removedColumns) || ! empty($diff->changedColumns)) {
597 357
                $sql[] = "CALL SYSPROC.ADMIN_CMD ('REORG TABLE " . $diff->getName($this)->getQuotedName($this) . "')";
598
            }
599
600 504
            $sql = array_merge($sql, $commentsSQL);
601
602 504
            if ($diff->newName !== false) {
603 42
                $sql[] =  'RENAME TABLE ' . $diff->getName($this)->getQuotedName($this) . ' TO ' . $diff->getNewName()->getQuotedName($this);
604
            }
605
606 504
            $sql = array_merge(
607 504
                $this->getPreAlterTableIndexForeignKeySQL($diff),
608 504
                $sql,
609 504
                $this->getPostAlterTableIndexForeignKeySQL($diff)
610
            );
611
        }
612
613 504
        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 336
    private function gatherAlterColumnSQL(Table $table, ColumnDiff $columnDiff, array &$sql, array &$queryParts)
625
    {
626 336
        $alterColumnClauses = $this->getAlterColumnClausesSQL($columnDiff);
627
628 336
        if (empty($alterColumnClauses)) {
629 42
            return;
630
        }
631
632
        // If we have a single column alteration, we can append the clause to the main query.
633 294
        if (count($alterColumnClauses) === 1) {
634 273
            $queryParts[] = current($alterColumnClauses);
635
636 273
            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 21
        foreach ($alterColumnClauses as $alterColumnClause) {
643 21
            $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ' . $alterColumnClause;
644
        }
645 21
    }
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 336
    private function getAlterColumnClausesSQL(ColumnDiff $columnDiff)
655
    {
656 336
        $column = $columnDiff->column->toArray();
657
658 336
        $alterClause = 'ALTER COLUMN ' . $columnDiff->column->getQuotedName($this);
659
660 336
        if ($column['columnDefinition']) {
661 21
            return [$alterClause . ' ' . $column['columnDefinition']];
662
        }
663
664 315
        $clauses = [];
665
666 315
        if ($columnDiff->hasChanged('type') ||
667 252
            $columnDiff->hasChanged('length') ||
668 231
            $columnDiff->hasChanged('precision') ||
669 210
            $columnDiff->hasChanged('scale') ||
670 315
            $columnDiff->hasChanged('fixed')
671
        ) {
672 168
            $clauses[] = $alterClause . ' SET DATA TYPE ' . $column['type']->getSQLDeclaration($column, $this);
673
        }
674
675 315
        if ($columnDiff->hasChanged('notnull')) {
676 84
            $clauses[] = $column['notnull'] ? $alterClause . ' SET NOT NULL' : $alterClause . ' DROP NOT NULL';
677
        }
678
679 315
        if ($columnDiff->hasChanged('default')) {
680 84
            if (isset($column['default'])) {
681 63
                $defaultClause = $this->getDefaultValueDeclarationSQL($column);
682
683 63
                if ($defaultClause) {
684 63
                    $clauses[] = $alterClause . ' SET' . $defaultClause;
685
                }
686
            } else {
687 21
                $clauses[] = $alterClause . ' DROP DEFAULT';
688
            }
689
        }
690
691 315
        return $clauses;
692
    }
693
694
    /**
695
     * {@inheritDoc}
696
     */
697 504
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
698
    {
699 504
        $sql   = [];
700 504
        $table = $diff->getName($this)->getQuotedName($this);
701
702 504
        foreach ($diff->removedIndexes as $remKey => $remIndex) {
703
            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 504
        $sql = array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff));
723
724 504
        return $sql;
725
    }
726
727
    /**
728
     * {@inheritdoc}
729
     */
730 105
    protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
731
    {
732 105
        if (strpos($tableName, '.') !== false) {
733 42
            [$schema]     = explode('.', $tableName);
734 42
            $oldIndexName = $schema . '.' . $oldIndexName;
735
        }
736
737 105
        return ['RENAME INDEX ' . $oldIndexName . ' TO ' . $index->getQuotedName($this)];
738
    }
739
740
    /**
741
     * {@inheritDoc}
742
     */
743 462
    public function getDefaultValueDeclarationSQL($field)
744
    {
745 462
        if (! empty($field['autoincrement'])) {
746 42
            return '';
747
        }
748
749 441
        if (isset($field['version']) && $field['version']) {
750
            if ((string) $field['type'] !== 'DateTime') {
751
                $field['default'] = '1';
752
            }
753
        }
754
755 441
        return parent::getDefaultValueDeclarationSQL($field);
756
    }
757
758
    /**
759
     * {@inheritDoc}
760
     */
761
    public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName)
762
    {
763
        return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (DEFAULT)';
764
    }
765
766
    /**
767
     * {@inheritDoc}
768
     */
769 21
    public function getCreateTemporaryTableSnippetSQL()
770
    {
771 21
        return 'DECLARE GLOBAL TEMPORARY TABLE';
772
    }
773
774
    /**
775
     * {@inheritDoc}
776
     */
777
    public function getTemporaryTableName($tableName)
778
    {
779
        return 'SESSION.' . $tableName;
780
    }
781
782
    /**
783
     * {@inheritDoc}
784
     */
785 42
    protected function doModifyLimitQuery($query, $limit, $offset = null)
786
    {
787 42
        $where = [];
788
789 42
        if ($offset > 0) {
790 21
            $where[] = sprintf('db22.DC_ROWNUM >= %d', $offset + 1);
791
        }
792
793 42
        if ($limit !== null) {
794 21
            $where[] = sprintf('db22.DC_ROWNUM <= %d', $offset + $limit);
795
        }
796
797 42
        if (empty($where)) {
798 42
            return $query;
799
        }
800
801
        // Todo OVER() needs ORDER BY data!
802 21
        return sprintf(
803 21
            'SELECT db22.* FROM (SELECT db21.*, ROW_NUMBER() OVER() AS DC_ROWNUM FROM (%s) db21) db22 WHERE %s',
804 21
            $query,
805 21
            implode(' AND ', $where)
806
        );
807
    }
808
809
    /**
810
     * {@inheritDoc}
811
     */
812 21
    public function getLocateExpression($str, $substr, $startPos = false)
813
    {
814 21
        if ($startPos === false) {
815 21
            return 'LOCATE(' . $substr . ', ' . $str . ')';
816
        }
817
818 21
        return 'LOCATE(' . $substr . ', ' . $str . ', ' . $startPos . ')';
819
    }
820
821
    /**
822
     * {@inheritDoc}
823
     */
824 21
    public function getSubstringExpression($value, $from, $length = null)
825
    {
826 21
        if ($length === null) {
827 21
            return 'SUBSTR(' . $value . ', ' . $from . ')';
828
        }
829
830 21
        return 'SUBSTR(' . $value . ', ' . $from . ', ' . $length . ')';
831
    }
832
833
    /**
834
     * {@inheritDoc}
835
     */
836 21
    public function supportsIdentityColumns()
837
    {
838 21
        return true;
839
    }
840
841
    /**
842
     * {@inheritDoc}
843
     */
844 21
    public function prefersIdentityColumns()
845
    {
846 21
        return true;
847
    }
848
849
    /**
850
     * {@inheritDoc}
851
     *
852
     * DB2 returns all column names in SQL result sets in uppercase.
853
     */
854 21
    public function getSQLResultCasing($column)
855
    {
856 21
        return strtoupper($column);
857
    }
858
859
    /**
860
     * {@inheritDoc}
861
     */
862 21
    public function getForUpdateSQL()
863
    {
864 21
        return ' WITH RR USE AND KEEP UPDATE LOCKS';
865
    }
866
867
    /**
868
     * {@inheritDoc}
869
     */
870
    public function getDummySelectSQL()
871
    {
872
        $expression = func_num_args() > 0 ? func_get_arg(0) : '1';
873
874
        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 21
    public function supportsSavepoints()
885
    {
886 21
        return false;
887
    }
888
889
    /**
890
     * {@inheritDoc}
891
     */
892 1092
    protected function getReservedKeywordsClass()
893
    {
894 1092
        return Keywords\DB2Keywords::class;
895
    }
896
}
897