Passed
Pull Request — master (#3133)
by Sergei
05:29
created

DB2Platform::getCharMaxLength()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

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