Passed
Pull Request — master (#3133)
by Michael
17:35
created

DB2Platform::getCurrentDateSQL()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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