Passed
Pull Request — master (#3135)
by Michael
17:53
created

DB2Platform::getAlterTableSQL()   F

Complexity

Conditions 21
Paths 2430

Size

Total Lines 101
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 52
CRAP Score 21.1605

Importance

Changes 0
Metric Value
dl 0
loc 101
ccs 52
cts 56
cp 0.9286
rs 2
c 0
b 0
f 0
cc 21
eloc 55
nc 2430
nop 1
crap 21.1605

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\DBAL\Platforms;
21
22
use Doctrine\DBAL\DBALException;
23
use Doctrine\DBAL\Schema\ColumnDiff;
24
use Doctrine\DBAL\Schema\Identifier;
25
use Doctrine\DBAL\Schema\Index;
26
use Doctrine\DBAL\Schema\Table;
27
use Doctrine\DBAL\Schema\TableDiff;
28
use Doctrine\DBAL\Types\Type;
29
use function array_merge;
30
use function count;
31
use function current;
32
use function explode;
33
use function func_get_arg;
34
use function func_num_args;
35
use function implode;
36
use function sprintf;
37
use function strpos;
38
use function strtoupper;
39
40
class DB2Platform extends AbstractPlatform
41
{
42
    /**
43
     * {@inheritdoc}
44
     */
45 36
    public function getBinaryMaxLength()
46
    {
47 36
        return 32704;
48
    }
49
50
    /**
51
     * {@inheritdoc}
52
     */
53 36
    public function getBinaryDefaultLength()
54
    {
55 36
        return 1;
56
    }
57
58
    /**
59
     * {@inheritDoc}
60
     */
61 36
    public function getBlobTypeDeclarationSQL(array $field)
62
    {
63
        // todo blob(n) with $field['length'];
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

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

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

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

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

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