Completed
Push — master ( 15a9ef...af81c1 )
by Sergei
26:08
created

DB2Platform::supportsReleaseSavepoints()   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 34
    public function getBinaryMaxLength()
46
    {
47 34
        return 32704;
48
    }
49
50
    /**
51
     * {@inheritdoc}
52
     */
53 34
    public function getBinaryDefaultLength()
54
    {
55 34
        return 1;
56
    }
57
58
    /**
59
     * {@inheritDoc}
60
     */
61 34
    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 34
        return 'BLOB(1M)';
65
    }
66
67
    /**
68
     * {@inheritDoc}
69
     */
70 85
    public function initializeDoctrineTypeMappings()
71
    {
72 85
        $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 85
    }
90
91
    /**
92
     * {@inheritdoc}
93
     */
94 714
    public function isCommentedDoctrineType(Type $doctrineType)
95
    {
96 714
        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 17
            return true;
100
        }
101
102 697
        return parent::isCommentedDoctrineType($doctrineType);
103
    }
104
105
    /**
106
     * {@inheritDoc}
107
     */
108 255
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
109
    {
110 255
        return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
111 255
                : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)');
112
    }
113
114
    /**
115
     * {@inheritdoc}
116
     */
117 17
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
118
    {
119 17
        return $fixed ? 'BINARY(' . ($length ?: 255) . ')' : 'VARBINARY(' . ($length ?: 255) . ')';
120
    }
121
122
    /**
123
     * {@inheritDoc}
124
     */
125 51
    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 51
        return 'CLOB(1M)';
129
    }
130
131
    /**
132
     * {@inheritDoc}
133
     */
134 51
    public function getName()
135
    {
136 51
        return 'db2';
137
    }
138
139
    /**
140
     * {@inheritDoc}
141
     */
142 34
    public function getBooleanTypeDeclarationSQL(array $columnDef)
143
    {
144 34
        return 'SMALLINT';
145
    }
146
147
    /**
148
     * {@inheritDoc}
149
     */
150 204
    public function getIntegerTypeDeclarationSQL(array $columnDef)
151
    {
152 204
        return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
153
    }
154
155
    /**
156
     * {@inheritDoc}
157
     */
158 17
    public function getBigIntTypeDeclarationSQL(array $columnDef)
159
    {
160 17
        return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
161
    }
162
163
    /**
164
     * {@inheritDoc}
165
     */
166 17
    public function getSmallIntTypeDeclarationSQL(array $columnDef)
167
    {
168 17
        return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
169
    }
170
171
    /**
172
     * {@inheritDoc}
173
     */
174 204
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
175
    {
176 204
        $autoinc = '';
177 204
        if ( ! empty($columnDef['autoincrement'])) {
178 34
            $autoinc = ' GENERATED BY DEFAULT AS IDENTITY';
179
        }
180
181 204
        return $autoinc;
182
    }
183
184
    /**
185
     * {@inheritdoc}
186
     */
187 17
    public function getBitAndComparisonExpression($value1, $value2)
188
    {
189 17
        return 'BITAND(' . $value1 . ', ' . $value2 . ')';
190
    }
191
192
    /**
193
     * {@inheritdoc}
194
     */
195 17
    public function getBitOrComparisonExpression($value1, $value2)
196
    {
197 17
        return 'BITOR(' . $value1 . ', ' . $value2 . ')';
198
    }
199
200
    /**
201
     * {@inheritdoc}
202
     */
203 17
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
204
    {
205
        switch ($unit) {
206 17
            case DateIntervalUnit::WEEK:
207 17
                $interval *= 7;
208 17
                $unit      = DateIntervalUnit::DAY;
209 17
                break;
210
211 17
            case DateIntervalUnit::QUARTER:
212 17
                $interval *= 3;
213 17
                $unit      = DateIntervalUnit::MONTH;
214 17
                break;
215
        }
216
217 17
        return $date . ' ' . $operator . ' ' . $interval . ' ' . $unit;
218
    }
219
220
    /**
221
     * {@inheritdoc}
222
     */
223 17
    public function getDateDiffExpression($date1, $date2)
224
    {
225 17
        return 'DAYS(' . $date1 . ') - DAYS(' . $date2 . ')';
226
    }
227
228
    /**
229
     * {@inheritDoc}
230
     */
231 17
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
232
    {
233 17
        if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) {
234 17
            return "TIMESTAMP(0) WITH DEFAULT";
235
        }
236
237 17
        return 'TIMESTAMP(0)';
238
    }
239
240
    /**
241
     * {@inheritDoc}
242
     */
243 17
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
244
    {
245 17
        return 'DATE';
246
    }
247
248
    /**
249
     * {@inheritDoc}
250
     */
251 17
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
252
    {
253 17
        return 'TIME';
254
    }
255
256
    /**
257
     * {@inheritdoc}
258
     */
259 34
    public function getTruncateTableSQL($tableName, $cascade = false)
260
    {
261 34
        $tableIdentifier = new Identifier($tableName);
262
263 34
        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 17
    public function getListTableColumnsSQL($table, $database = null)
277
    {
278 17
        $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 17
               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 17
    public function getListTableIndexesSQL($table, $currentDatabase = null)
344
    {
345 17
        $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 17
                WHERE    idx.TABNAME = UPPER(" . $table . ")
361
                ORDER BY idxcol.COLSEQ ASC";
362
    }
363
364
    /**
365
     * {@inheritDoc}
366
     */
367 17
    public function getListTableForeignKeysSQL($table)
368
    {
369 17
        $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 17
                WHERE    fk.TABNAME = UPPER(" . $table . ")
395
                ORDER BY fkcol.COLSEQ ASC";
396
    }
397
398
    /**
399
     * {@inheritDoc}
400
     */
401 17
    public function getCreateViewSQL($name, $sql)
402
    {
403 17
        return "CREATE VIEW ".$name." AS ".$sql;
404
    }
405
406
    /**
407
     * {@inheritDoc}
408
     */
409 17
    public function getDropViewSQL($name)
410
    {
411 17
        return "DROP VIEW ".$name;
412
    }
413
414
    /**
415
     * {@inheritDoc}
416
     */
417 17
    public function getCreateDatabaseSQL($database)
418
    {
419 17
        return "CREATE DATABASE ".$database;
420
    }
421
422
    /**
423
     * {@inheritDoc}
424
     */
425 17
    public function getDropDatabaseSQL($database)
426
    {
427 17
        return "DROP DATABASE " . $database;
428
    }
429
430
    /**
431
     * {@inheritDoc}
432
     */
433 17
    public function supportsCreateDropDatabase()
434
    {
435 17
        return false;
436
    }
437
438
    /**
439
     * {@inheritDoc}
440
     */
441 17
    public function supportsReleaseSavepoints()
442
    {
443 17
        return false;
444
    }
445
446
    /**
447
     * {@inheritdoc}
448
     */
449 221
    public function supportsCommentOnStatement()
450
    {
451 221
        return true;
452
    }
453
454
    /**
455
     * {@inheritDoc}
456
     */
457 34
    public function getCurrentDateSQL()
458
    {
459 34
        return 'CURRENT DATE';
460
    }
461
462
    /**
463
     * {@inheritDoc}
464
     */
465 17
    public function getCurrentTimeSQL()
466
    {
467 17
        return 'CURRENT TIME';
468
    }
469
470
    /**
471
     * {@inheritDoc}
472
     */
473 34
    public function getCurrentTimestampSQL()
474
    {
475 34
        return "CURRENT TIMESTAMP";
476
    }
477
478
    /**
479
     * {@inheritDoc}
480
     */
481 17
    public function getIndexDeclarationSQL($name, Index $index)
482
    {
483
        // Index declaration in statements like CREATE TABLE is not supported.
484 17
        throw DBALException::notSupported(__METHOD__);
485
    }
486
487
    /**
488
     * {@inheritDoc}
489
     */
490 204
    protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
491
    {
492 204
        $indexes = [];
493 204
        if (isset($options['indexes'])) {
494 204
            $indexes = $options['indexes'];
495
        }
496 204
        $options['indexes'] = [];
497
498 204
        $sqls = parent::_getCreateTableSQL($tableName, $columns, $options);
499
500 204
        foreach ($indexes as $definition) {
501 68
            $sqls[] = $this->getCreateIndexSQL($definition, $tableName);
502
        }
503 204
        return $sqls;
504
    }
505
506
    /**
507
     * {@inheritDoc}
508
     */
509 408
    public function getAlterTableSQL(TableDiff $diff)
510
    {
511 408
        $sql = [];
512 408
        $columnSql = [];
513 408
        $commentsSQL = [];
514
515 408
        $queryParts = [];
516 408
        foreach ($diff->addedColumns as $column) {
517 68
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
518
                continue;
519
            }
520
521 68
            $columnDef = $column->toArray();
522 68
            $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 68
            if ( ! empty($columnDef['notnull']) &&
526 68
                ! isset($columnDef['default']) &&
527 68
                empty($columnDef['autoincrement'])
528
            ) {
529 51
                $queryPart .= ' WITH DEFAULT';
530
            }
531
532 68
            $queryParts[] = $queryPart;
533
534 68
            $comment = $this->getColumnComment($column);
535
536 68
            if (null !== $comment && '' !== $comment) {
537 17
                $commentsSQL[] = $this->getCommentOnColumnSQL(
538 17
                    $diff->getName($this)->getQuotedName($this),
539 17
                    $column->getQuotedName($this),
540 68
                    $comment
541
                );
542
            }
543
        }
544
545 408
        foreach ($diff->removedColumns as $column) {
546 51
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
547
                continue;
548
            }
549
550 51
            $queryParts[] =  'DROP COLUMN ' . $column->getQuotedName($this);
551
        }
552
553 408
        foreach ($diff->changedColumns as $columnDiff) {
554 289
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
555
                continue;
556
            }
557
558 289
            if ($columnDiff->hasChanged('comment')) {
559 17
                $commentsSQL[] = $this->getCommentOnColumnSQL(
560 17
                    $diff->getName($this)->getQuotedName($this),
561 17
                    $columnDiff->column->getQuotedName($this),
562 17
                    $this->getColumnComment($columnDiff->column)
563
                );
564
565 17
                if (count($columnDiff->changedProperties) === 1) {
566 17
                    continue;
567
                }
568
            }
569
570 272
            $this->gatherAlterColumnSQL($diff->fromTable, $columnDiff, $sql, $queryParts);
571
        }
572
573 408
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
574 68
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
575
                continue;
576
            }
577
578 68
            $oldColumnName = new Identifier($oldColumnName);
579
580 68
            $queryParts[] =  'RENAME COLUMN ' . $oldColumnName->getQuotedName($this) .
581 68
                ' TO ' . $column->getQuotedName($this);
582
        }
583
584 408
        $tableSql = [];
585
586 408
        if ( ! $this->onSchemaAlterTable($diff, $tableSql)) {
587 408
            if (count($queryParts) > 0) {
588 306
                $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(" ", $queryParts);
589
            }
590
591
            // Some table alteration operations require a table reorganization.
592 408
            if ( ! empty($diff->removedColumns) || ! empty($diff->changedColumns)) {
593 289
                $sql[] = "CALL SYSPROC.ADMIN_CMD ('REORG TABLE " . $diff->getName($this)->getQuotedName($this) . "')";
594
            }
595
596 408
            $sql = array_merge($sql, $commentsSQL);
597
598 408
            if ($diff->newName !== false) {
599 34
                $sql[] =  'RENAME TABLE ' . $diff->getName($this)->getQuotedName($this) . ' TO ' . $diff->getNewName()->getQuotedName($this);
600
            }
601
602 408
            $sql = array_merge(
603 408
                $this->getPreAlterTableIndexForeignKeySQL($diff),
604 408
                $sql,
605 408
                $this->getPostAlterTableIndexForeignKeySQL($diff)
606
            );
607
        }
608
609 408
        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 272
    private function gatherAlterColumnSQL(Table $table, ColumnDiff $columnDiff, array &$sql, array &$queryParts)
621
    {
622 272
        $alterColumnClauses = $this->getAlterColumnClausesSQL($columnDiff);
623
624 272
        if (empty($alterColumnClauses)) {
625 34
            return;
626
        }
627
628
        // If we have a single column alteration, we can append the clause to the main query.
629 238
        if (count($alterColumnClauses) === 1) {
630 221
            $queryParts[] = current($alterColumnClauses);
631
632 221
            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 17
        foreach ($alterColumnClauses as $alterColumnClause) {
639 17
            $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ' . $alterColumnClause;
640
        }
641 17
    }
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 272
    private function getAlterColumnClausesSQL(ColumnDiff $columnDiff)
651
    {
652 272
        $column = $columnDiff->column->toArray();
653
654 272
        $alterClause = 'ALTER COLUMN ' . $columnDiff->column->getQuotedName($this);
655
656 272
        if ($column['columnDefinition']) {
657 17
            return [$alterClause . ' ' . $column['columnDefinition']];
658
        }
659
660 255
        $clauses = [];
661
662 255
        if ($columnDiff->hasChanged('type') ||
663 204
            $columnDiff->hasChanged('length') ||
664 187
            $columnDiff->hasChanged('precision') ||
665 170
            $columnDiff->hasChanged('scale') ||
666 255
            $columnDiff->hasChanged('fixed')
667
        ) {
668 136
            $clauses[] = $alterClause . ' SET DATA TYPE ' . $column['type']->getSQLDeclaration($column, $this);
669
        }
670
671 255
        if ($columnDiff->hasChanged('notnull')) {
672 68
            $clauses[] = $column['notnull'] ? $alterClause . ' SET NOT NULL' : $alterClause . ' DROP NOT NULL';
673
        }
674
675 255
        if ($columnDiff->hasChanged('default')) {
676 68
            if (isset($column['default'])) {
677 51
                $defaultClause = $this->getDefaultValueDeclarationSQL($column);
678
679 51
                if ($defaultClause) {
680 51
                    $clauses[] = $alterClause . ' SET' . $defaultClause;
681
                }
682
            } else {
683 17
                $clauses[] = $alterClause . ' DROP DEFAULT';
684
            }
685
        }
686
687 255
        return $clauses;
688
    }
689
690
    /**
691
     * {@inheritDoc}
692
     */
693 408
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
694
    {
695 408
        $sql = [];
696 408
        $table = $diff->getName($this)->getQuotedName($this);
697
698 408
        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 408
        $sql = array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff));
719
720 408
        return $sql;
721
    }
722
723
    /**
724
     * {@inheritdoc}
725
     */
726 85
    protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
727
    {
728 85
        if (strpos($tableName, '.') !== false) {
729 34
            list($schema) = explode('.', $tableName);
730 34
            $oldIndexName = $schema . '.' . $oldIndexName;
731
        }
732
733 85
        return ['RENAME INDEX ' . $oldIndexName . ' TO ' . $index->getQuotedName($this)];
734
    }
735
736
    /**
737
     * {@inheritDoc}
738
     */
739 374
    public function getDefaultValueDeclarationSQL($field)
740
    {
741 374
        if ( ! empty($field['autoincrement'])) {
742 34
            return '';
743
        }
744
745 357
        if (isset($field['version']) && $field['version']) {
746
            if ((string) $field['type'] != "DateTime") {
747
                $field['default'] = "1";
748
            }
749
        }
750
751 357
        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 17
    public function getCreateTemporaryTableSnippetSQL()
766
    {
767 17
        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 17
    protected function doModifyLimitQuery($query, $limit, $offset = null)
782
    {
783 17
        $where = [];
784
785 17
        if ($offset > 0) {
786 17
            $where[] = sprintf('db22.DC_ROWNUM >= %d', $offset + 1);
787
        }
788
789 17
        if ($limit !== null) {
790 17
            $where[] = sprintf('db22.DC_ROWNUM <= %d', $offset + $limit);
791
        }
792
793 17
        if (empty($where)) {
794 17
            return $query;
795
        }
796
797
        // Todo OVER() needs ORDER BY data!
798 17
        return sprintf(
799 17
            'SELECT db22.* FROM (SELECT db21.*, ROW_NUMBER() OVER() AS DC_ROWNUM FROM (%s) db21) db22 WHERE %s',
800 17
            $query,
801 17
            implode(' AND ', $where)
802
        );
803
    }
804
805
    /**
806
     * {@inheritDoc}
807
     */
808 17
    public function getLocateExpression($str, $substr, $startPos = false)
809
    {
810 17
        if ($startPos == false) {
811 17
            return 'LOCATE(' . $substr . ', ' . $str . ')';
812
        }
813
814 17
        return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')';
815
    }
816
817
    /**
818
     * {@inheritDoc}
819
     */
820 17
    public function getSubstringExpression($value, $from, $length = null)
821
    {
822 17
        if ($length === null) {
823 17
            return 'SUBSTR(' . $value . ', ' . $from . ')';
824
        }
825
826 17
        return 'SUBSTR(' . $value . ', ' . $from . ', ' . $length . ')';
827
    }
828
829
    /**
830
     * {@inheritDoc}
831
     */
832 17
    public function supportsIdentityColumns()
833
    {
834 17
        return true;
835
    }
836
837
    /**
838
     * {@inheritDoc}
839
     */
840 17
    public function prefersIdentityColumns()
841
    {
842 17
        return true;
843
    }
844
845
    /**
846
     * {@inheritDoc}
847
     *
848
     * DB2 returns all column names in SQL result sets in uppercase.
849
     */
850 17
    public function getSQLResultCasing($column)
851
    {
852 17
        return strtoupper($column);
853
    }
854
855
    /**
856
     * {@inheritDoc}
857
     */
858 17
    public function getForUpdateSQL()
859
    {
860 17
        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 17
    public function supportsSavepoints()
881
    {
882 17
        return false;
883
    }
884
885
    /**
886
     * {@inheritDoc}
887
     */
888 884
    protected function getReservedKeywordsClass()
889
    {
890 884
        return Keywords\DB2Keywords::class;
891
    }
892
}
893