Passed
Pull Request — master (#3133)
by Sergei
65:15
created

DB2Platform::getDummySelectSQL()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

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