Failed Conditions
Pull Request — master (#3372)
by Benjamin
11:15
created

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