Completed
Push — master ( 6d673d...7f4ef4 )
by Sergei
40:47 queued 40:43
created

DB2Platform::getCreateDatabaseSQL()   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 1
crap 1
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 27
    public function getBlobTypeDeclarationSQL(array $field) : string
29
    {
30
        // todo blob(n) with $field['length'];
31 27
        return 'BLOB(1M)';
32
    }
33
34
    /**
35
     * {@inheritDoc}
36
     */
37 135
    public function initializeDoctrineTypeMappings() : void
38
    {
39 135
        $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 135
    }
57
58
    /**
59
     * {@inheritdoc}
60
     */
61 1107
    public function isCommentedDoctrineType(Type $doctrineType) : bool
62
    {
63 1107
        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 27
            return true;
67
        }
68
69 1080
        return parent::isCommentedDoctrineType($doctrineType);
70
    }
71
72
    /**
73
     * {@inheritDoc}
74
     */
75 54
    protected function getBinaryTypeDeclarationSQLSnippet(?int $length) : string
76
    {
77 54
        return $this->getCharTypeDeclarationSQLSnippet($length) . ' FOR BIT DATA';
78
    }
79
80
    /**
81
     * {@inheritDoc}
82
     */
83 54
    protected function getVarbinaryTypeDeclarationSQLSnippet(?int $length) : string
84
    {
85 54
        return $this->getVarcharTypeDeclarationSQLSnippet($length) . ' FOR BIT DATA';
86
    }
87
88
    /**
89
     * {@inheritDoc}
90
     */
91 81
    public function getClobTypeDeclarationSQL(array $field) : string
92
    {
93
        // todo clob(n) with $field['length'];
94 81
        return 'CLOB(1M)';
95
    }
96
97
    /**
98
     * {@inheritDoc}
99
     */
100 135
    public function getName() : string
101
    {
102 135
        return 'db2';
103
    }
104
105
    /**
106
     * {@inheritDoc}
107
     */
108 54
    public function getBooleanTypeDeclarationSQL(array $columnDef) : string
109
    {
110 54
        return 'SMALLINT';
111
    }
112
113
    /**
114
     * {@inheritDoc}
115
     */
116 324
    public function getIntegerTypeDeclarationSQL(array $columnDef) : string
117
    {
118 324
        return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
119
    }
120
121
    /**
122
     * {@inheritDoc}
123
     */
124 27
    public function getBigIntTypeDeclarationSQL(array $columnDef) : string
125
    {
126 27
        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 324
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) : string
141
    {
142 324
        $autoinc = '';
143 324
        if (! empty($columnDef['autoincrement'])) {
144 54
            $autoinc = ' GENERATED BY DEFAULT AS IDENTITY';
145
        }
146
147 324
        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 27
    protected function getDateArithmeticIntervalExpression(string $date, string $operator, string $interval, string $unit) : string
170
    {
171
        switch ($unit) {
172 27
            case DateIntervalUnit::WEEK:
173 27
                $interval = $this->multiplyInterval($interval, 7);
174 27
                $unit     = DateIntervalUnit::DAY;
175 27
                break;
176
177 27
            case DateIntervalUnit::QUARTER:
178 27
                $interval = $this->multiplyInterval($interval, 3);
179 27
                $unit     = DateIntervalUnit::MONTH;
180 27
                break;
181
        }
182
183 27
        return $date . ' ' . $operator . ' ' . $interval . ' ' . $unit;
184
    }
185
186
    /**
187
     * {@inheritdoc}
188
     */
189 27
    public function getDateDiffExpression(string $date1, string $date2) : string
190
    {
191 27
        return 'DAYS(' . $date1 . ') - DAYS(' . $date2 . ')';
192
    }
193
194
    /**
195
     * {@inheritDoc}
196
     */
197 27
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) : string
198
    {
199 27
        if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] === true) {
200 27
            return 'TIMESTAMP(0) WITH DEFAULT';
201
        }
202
203 27
        return 'TIMESTAMP(0)';
204
    }
205
206
    /**
207
     * {@inheritDoc}
208
     */
209 27
    public function getDateTypeDeclarationSQL(array $fieldDeclaration) : string
210
    {
211 27
        return 'DATE';
212
    }
213
214
    /**
215
     * {@inheritDoc}
216
     */
217 27
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration) : string
218
    {
219 27
        return 'TIME';
220
    }
221
222
    /**
223
     * {@inheritdoc}
224
     */
225 54
    public function getTruncateTableSQL(string $tableName, bool $cascade = false) : string
226
    {
227 54
        $tableIdentifier = new Identifier($tableName);
228
229 54
        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 27
    public function getListTableColumnsSQL(string $table, ?string $database = null) : string
236
    {
237 27
        $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 27
               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
    public function getListTablesSQL() : string
287
    {
288
        return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = 'T'";
289
    }
290
291
    /**
292
     * {@inheritDoc}
293
     */
294
    public function getListViewsSQL(string $database) : string
295
    {
296
        return 'SELECT NAME, TEXT FROM SYSIBM.SYSVIEWS';
297
    }
298
299
    /**
300
     * {@inheritDoc}
301
     */
302 27
    public function getListTableIndexesSQL(string $table, ?string $currentDatabase = null) : string
303
    {
304 27
        $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 27
                WHERE    idx.TABNAME = UPPER(" . $table . ')
320
                ORDER BY idxcol.COLSEQ ASC';
321
    }
322
323
    /**
324
     * {@inheritDoc}
325
     */
326 27
    public function getListTableForeignKeysSQL(string $table, ?string $database = null) : string
327
    {
328 27
        $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 27
                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 27
    public function getCreateDatabaseSQL(string $database) : string
377
    {
378 27
        return 'CREATE DATABASE ' . $database;
379
    }
380
381
    /**
382
     * {@inheritDoc}
383
     */
384 27
    public function getDropDatabaseSQL(string $database) : string
385
    {
386 27
        return 'DROP DATABASE ' . $database;
387
    }
388
389
    /**
390
     * {@inheritDoc}
391
     */
392 27
    public function supportsCreateDropDatabase() : bool
393
    {
394 27
        return false;
395
    }
396
397
    /**
398
     * {@inheritDoc}
399
     */
400 27
    public function supportsReleaseSavepoints() : bool
401
    {
402 27
        return false;
403
    }
404
405
    /**
406
     * {@inheritdoc}
407
     */
408 351
    public function supportsCommentOnStatement() : bool
409
    {
410 351
        return true;
411
    }
412
413
    /**
414
     * {@inheritDoc}
415
     */
416 54
    public function getCurrentDateSQL() : string
417
    {
418 54
        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 54
    public function getCurrentTimestampSQL() : string
433
    {
434 54
        return 'CURRENT TIMESTAMP';
435
    }
436
437
    /**
438
     * {@inheritDoc}
439
     */
440 27
    public function getIndexDeclarationSQL(string $name, Index $index) : string
441
    {
442
        // Index declaration in statements like CREATE TABLE is not supported.
443 27
        throw NotSupported::new(__METHOD__);
444
    }
445
446
    /**
447
     * {@inheritDoc}
448
     */
449 324
    protected function _getCreateTableSQL(string $tableName, array $columns, array $options = []) : array
450
    {
451 324
        $indexes = [];
452 324
        if (isset($options['indexes'])) {
453 324
            $indexes = $options['indexes'];
454
        }
455 324
        $options['indexes'] = [];
456
457 324
        $sqls = parent::_getCreateTableSQL($tableName, $columns, $options);
458
459 324
        foreach ($indexes as $definition) {
460 108
            $sqls[] = $this->getCreateIndexSQL($definition, $tableName);
461
        }
462
463 324
        return $sqls;
464
    }
465
466
    /**
467
     * {@inheritDoc}
468
     */
469 648
    public function getAlterTableSQL(TableDiff $diff) : array
470
    {
471 648
        $sql         = [];
472 648
        $columnSql   = [];
473 648
        $commentsSQL = [];
474
475 648
        $queryParts = [];
476 648
        foreach ($diff->addedColumns as $column) {
477 108
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
478
                continue;
479
            }
480
481 108
            $columnDef = $column->toArray();
482 108
            $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 108
            if (! empty($columnDef['notnull']) &&
486 108
                ! isset($columnDef['default']) &&
487 108
                empty($columnDef['autoincrement'])
488
            ) {
489 81
                $queryPart .= ' WITH DEFAULT';
490
            }
491
492 108
            $queryParts[] = $queryPart;
493
494 108
            $comment = $this->getColumnComment($column);
495
496 108
            if ($comment === null || $comment === '') {
497 81
                continue;
498
            }
499
500 27
            $commentsSQL[] = $this->getCommentOnColumnSQL(
501 27
                $diff->getName($this)->getQuotedName($this),
502 27
                $column->getQuotedName($this),
503
                $comment
504
            );
505
        }
506
507 648
        foreach ($diff->removedColumns as $column) {
508 81
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
509
                continue;
510
            }
511
512 81
            $queryParts[] =  'DROP COLUMN ' . $column->getQuotedName($this);
513
        }
514
515 648
        foreach ($diff->changedColumns as $columnDiff) {
516 459
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
517
                continue;
518
            }
519
520 459
            if ($columnDiff->hasChanged('comment')) {
521 27
                $commentsSQL[] = $this->getCommentOnColumnSQL(
522 27
                    $diff->getName($this)->getQuotedName($this),
523 27
                    $columnDiff->column->getQuotedName($this),
524 27
                    $this->getColumnComment($columnDiff->column)
525
                );
526
527 27
                if (count($columnDiff->changedProperties) === 1) {
528 27
                    continue;
529
                }
530
            }
531
532 432
            $this->gatherAlterColumnSQL($diff->getName($this), $columnDiff, $sql, $queryParts);
533
        }
534
535 648
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
536 108
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
537
                continue;
538
            }
539
540 108
            $oldColumnName = new Identifier($oldColumnName);
541
542 108
            $queryParts[] =  'RENAME COLUMN ' . $oldColumnName->getQuotedName($this) .
543 108
                ' TO ' . $column->getQuotedName($this);
544
        }
545
546 648
        $tableSql = [];
547
548 648
        if (! $this->onSchemaAlterTable($diff, $tableSql)) {
549 648
            if (count($queryParts) > 0) {
550 486
                $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(' ', $queryParts);
551
            }
552
553
            // Some table alteration operations require a table reorganization.
554 648
            if (! empty($diff->removedColumns) || ! empty($diff->changedColumns)) {
555 459
                $sql[] = "CALL SYSPROC.ADMIN_CMD ('REORG TABLE " . $diff->getName($this)->getQuotedName($this) . "')";
556
            }
557
558 648
            $sql = array_merge($sql, $commentsSQL);
559
560 648
            $newName = $diff->getNewName();
561
562 648
            if ($newName !== null) {
563 54
                $sql[] = sprintf(
564 4
                    'RENAME TABLE %s TO %s',
565 54
                    $diff->getName($this)->getQuotedName($this),
566 54
                    $newName->getQuotedName($this)
567
                );
568
            }
569
570 648
            $sql = array_merge(
571 648
                $this->getPreAlterTableIndexForeignKeySQL($diff),
572 648
                $sql,
573 648
                $this->getPostAlterTableIndexForeignKeySQL($diff)
574
            );
575
        }
576
577 648
        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 432
    private function gatherAlterColumnSQL(Identifier $table, ColumnDiff $columnDiff, array &$sql, array &$queryParts) : void
589
    {
590 432
        $alterColumnClauses = $this->getAlterColumnClausesSQL($columnDiff);
591
592 432
        if (empty($alterColumnClauses)) {
593 54
            return;
594
        }
595
596
        // If we have a single column alteration, we can append the clause to the main query.
597 378
        if (count($alterColumnClauses) === 1) {
598 351
            $queryParts[] = current($alterColumnClauses);
599
600 351
            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 27
        foreach ($alterColumnClauses as $alterColumnClause) {
607 27
            $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ' . $alterColumnClause;
608
        }
609 27
    }
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 432
    private function getAlterColumnClausesSQL(ColumnDiff $columnDiff) : array
619
    {
620 432
        $column = $columnDiff->column->toArray();
621
622 432
        $alterClause = 'ALTER COLUMN ' . $columnDiff->column->getQuotedName($this);
623
624 432
        if ($column['columnDefinition']) {
625 27
            return [$alterClause . ' ' . $column['columnDefinition']];
626
        }
627
628 405
        $clauses = [];
629
630 405
        if ($columnDiff->hasChanged('type') ||
631 399
            $columnDiff->hasChanged('length') ||
632 397
            $columnDiff->hasChanged('precision') ||
633 395
            $columnDiff->hasChanged('scale') ||
634 405
            $columnDiff->hasChanged('fixed')
635
        ) {
636 216
            $clauses[] = $alterClause . ' SET DATA TYPE ' . $column['type']->getSQLDeclaration($column, $this);
637
        }
638
639 405
        if ($columnDiff->hasChanged('notnull')) {
640 108
            $clauses[] = $column['notnull'] ? $alterClause . ' SET NOT NULL' : $alterClause . ' DROP NOT NULL';
641
        }
642
643 405
        if ($columnDiff->hasChanged('default')) {
644 108
            if (isset($column['default'])) {
645 81
                $defaultClause = $this->getDefaultValueDeclarationSQL($column);
646
647 81
                if ($defaultClause) {
648 81
                    $clauses[] = $alterClause . ' SET' . $defaultClause;
649
                }
650
            } else {
651 27
                $clauses[] = $alterClause . ' DROP DEFAULT';
652
            }
653
        }
654
655 405
        return $clauses;
656
    }
657
658
    /**
659
     * {@inheritDoc}
660
     */
661 648
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) : array
662
    {
663 648
        $sql   = [];
664 648
        $table = $diff->getName($this)->getQuotedName($this);
665
666 648
        foreach ($diff->removedIndexes as $remKey => $remIndex) {
667
            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 648
        $sql = array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff));
687
688 648
        return $sql;
689
    }
690
691
    /**
692
     * {@inheritdoc}
693
     */
694 135
    protected function getRenameIndexSQL(string $oldIndexName, Index $index, string $tableName) : array
695
    {
696 135
        if (strpos($tableName, '.') !== false) {
697 54
            [$schema]     = explode('.', $tableName);
698 54
            $oldIndexName = $schema . '.' . $oldIndexName;
699
        }
700
701 135
        return ['RENAME INDEX ' . $oldIndexName . ' TO ' . $index->getQuotedName($this)];
702
    }
703
704
    /**
705
     * {@inheritDoc}
706
     */
707 594
    public function getDefaultValueDeclarationSQL(array $field) : string
708
    {
709 594
        if (! empty($field['autoincrement'])) {
710 54
            return '';
711
        }
712
713 567
        if (isset($field['version']) && $field['version']) {
714
            if ((string) $field['type'] !== 'DateTime') {
715
                $field['default'] = '1';
716
            }
717
        }
718
719 567
        return parent::getDefaultValueDeclarationSQL($field);
720
    }
721
722
    /**
723
     * {@inheritDoc}
724
     */
725
    public function getEmptyIdentityInsertSQL(string $tableName, string $identifierColumnName) : string
726
    {
727
        return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (DEFAULT)';
728
    }
729
730
    /**
731
     * {@inheritDoc}
732
     */
733 27
    public function getCreateTemporaryTableSnippetSQL() : string
734
    {
735 27
        return 'DECLARE GLOBAL TEMPORARY TABLE';
736
    }
737
738
    /**
739
     * {@inheritDoc}
740
     */
741
    public function getTemporaryTableName(string $tableName) : string
742
    {
743
        return 'SESSION.' . $tableName;
744
    }
745
746
    /**
747
     * {@inheritDoc}
748
     */
749 54
    protected function doModifyLimitQuery(string $query, ?int $limit, int $offset) : string
750
    {
751 54
        $where = [];
752
753 54
        if ($offset > 0) {
754 27
            $where[] = sprintf('db22.DC_ROWNUM >= %d', $offset + 1);
755
        }
756
757 54
        if ($limit !== null) {
758 27
            $where[] = sprintf('db22.DC_ROWNUM <= %d', $offset + $limit);
759
        }
760
761 54
        if (empty($where)) {
762 54
            return $query;
763
        }
764
765
        // Todo OVER() needs ORDER BY data!
766 27
        return sprintf(
767 2
            'SELECT db22.* FROM (SELECT db21.*, ROW_NUMBER() OVER() AS DC_ROWNUM FROM (%s) db21) db22 WHERE %s',
768 27
            $query,
769 27
            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 27
    public function getSubstringExpression(string $string, string $start, ?string $length = null) : string
789
    {
790 27
        if ($length === null) {
791 27
            return sprintf('SUBSTR(%s, %s)', $string, $start);
792
        }
793
794 27
        return sprintf('SUBSTR(%s, %s, %s)', $string, $start, $length);
795
    }
796
797
    /**
798
     * {@inheritDoc}
799
     */
800
    public function getCurrentDatabaseExpression() : string
801
    {
802
        return 'CURRENT_USER';
803
    }
804
805
    /**
806
     * {@inheritDoc}
807
     */
808 27
    public function supportsIdentityColumns() : bool
809
    {
810 27
        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 27
    public function getSQLResultCasing(string $column) : string
827
    {
828 27
        return strtoupper($column);
829
    }
830
831
    /**
832
     * {@inheritDoc}
833
     */
834 27
    public function getForUpdateSQL() : string
835
    {
836 27
        return ' WITH RR USE AND KEEP UPDATE LOCKS';
837
    }
838
839
    /**
840
     * {@inheritDoc}
841
     */
842
    public function getDummySelectSQL(string $expression = '1') : string
843
    {
844
        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 27
    public function supportsSavepoints() : bool
855
    {
856 27
        return false;
857
    }
858
859
    /**
860
     * {@inheritDoc}
861
     */
862 1404
    protected function getReservedKeywordsClass() : string
863
    {
864 1404
        return Keywords\DB2Keywords::class;
865
    }
866
867
    public function getListTableCommentsSQL(string $table) : string
868
    {
869
        return sprintf(
870
            <<<'SQL'
871
SELECT REMARKS
872
  FROM SYSIBM.SYSTABLES
873
  WHERE NAME = UPPER( %s )
874
SQL
875
            ,
876
            $this->quoteStringLiteral($table)
877
        );
878
    }
879
}
880