Completed
Pull Request — master (#3833)
by Benjamin
17:15
created

DB2Platform::getListTableCommentsSQL()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1.0156

Importance

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