Completed
Pull Request — 2.10.x (#4009)
by Grégoire
08:50
created

DB2Platform::getPreAlterTableIndexForeignKeySQL()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 13.3261

Importance

Changes 0
Metric Value
dl 0
loc 31
ccs 7
cts 17
cp 0.4118
rs 8.8017
c 0
b 0
f 0
cc 6
nc 6
nop 1
crap 13.3261
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\TableDiff;
10
use Doctrine\DBAL\Types\Type;
11
use Doctrine\DBAL\Types\Types;
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 1883
    public function getCharMaxLength() : int
26
    {
27 1883
        return 254;
28
    }
29
30
    /**
31
     * {@inheritdoc}
32
     */
33 1597
    public function getBinaryMaxLength()
34
    {
35 1597
        return 32704;
36
    }
37
38
    /**
39
     * {@inheritdoc}
40
     */
41 1566
    public function getBinaryDefaultLength()
42
    {
43 1566
        return 1;
44
    }
45
46
    /**
47
     * {@inheritDoc}
48
     */
49 1985
    public function getVarcharTypeDeclarationSQL(array $field)
50
    {
51
        // for IBM DB2, the CHAR max length is less than VARCHAR default length
52 1985
        if (! isset($field['length']) && ! empty($field['fixed'])) {
53 1840
            $field['length'] = $this->getCharMaxLength();
54
        }
55
56 1985
        return parent::getVarcharTypeDeclarationSQL($field);
57
    }
58
59
    /**
60
     * {@inheritDoc}
61
     */
62 1917
    public function getBlobTypeDeclarationSQL(array $field)
63
    {
64
        // todo blob(n) with $field['length'];
65 1917
        return 'BLOB(1M)';
66
    }
67
68
    /**
69
     * {@inheritDoc}
70
     */
71 1845
    public function initializeDoctrineTypeMappings()
72
    {
73 1845
        $this->doctrineTypeMapping = [
0 ignored issues
show
Documentation Bug introduced by
It seems like array('smallint' => 'sma...mestamp' => 'datetime') of type array<string,string,{"sm...,"timestamp":"string"}> is incompatible with the declared type array<integer,string>|null of property $doctrineTypeMapping.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
74
            'smallint'      => 'smallint',
75
            'bigint'        => 'bigint',
76
            'integer'       => 'integer',
77
            'time'          => 'time',
78
            'date'          => 'date',
79
            'varchar'       => 'string',
80
            'character'     => 'string',
81
            'varbinary'     => 'binary',
82
            'binary'        => 'binary',
83
            'clob'          => 'text',
84
            'blob'          => 'blob',
85
            'decimal'       => 'decimal',
86
            'double'        => 'float',
87
            'real'          => 'float',
88
            'timestamp'     => 'datetime',
89
        ];
90 1845
    }
91
92
    /**
93
     * {@inheritdoc}
94
     */
95 2023
    public function isCommentedDoctrineType(Type $doctrineType)
96
    {
97 2023
        if ($doctrineType->getName() === Types::BOOLEAN) {
98
            // We require a commented boolean type in order to distinguish between boolean and smallint
99
            // as both (have to) map to the same native type.
100 1180
            return true;
101
        }
102
103 2022
        return parent::isCommentedDoctrineType($doctrineType);
104
    }
105
106
    /**
107
     * {@inheritDoc}
108
     */
109 1986
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
110
    {
111 1986
        return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(254)')
112 1986
                : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)');
113
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118 1595
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
119
    {
120 1595
        return $this->getVarcharTypeDeclarationSQLSnippet($length, $fixed) . ' FOR BIT DATA';
121
    }
122
123
    /**
124
     * {@inheritDoc}
125
     */
126 1918
    public function getClobTypeDeclarationSQL(array $field)
127
    {
128
        // todo clob(n) with $field['length'];
129 1918
        return 'CLOB(1M)';
130
    }
131
132
    /**
133
     * {@inheritDoc}
134
     */
135 2001
    public function getName()
136
    {
137 2001
        return 'db2';
138
    }
139
140
    /**
141
     * {@inheritDoc}
142
     */
143 1863
    public function getBooleanTypeDeclarationSQL(array $columnDef)
144
    {
145 1863
        return 'SMALLINT';
146
    }
147
148
    /**
149
     * {@inheritDoc}
150
     */
151 1993
    public function getIntegerTypeDeclarationSQL(array $columnDef)
152
    {
153 1993
        return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
154
    }
155
156
    /**
157
     * {@inheritDoc}
158
     */
159 1786
    public function getBigIntTypeDeclarationSQL(array $columnDef)
160
    {
161 1786
        return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
162
    }
163
164
    /**
165
     * {@inheritDoc}
166
     */
167 1840
    public function getSmallIntTypeDeclarationSQL(array $columnDef)
168
    {
169 1840
        return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
170
    }
171
172
    /**
173
     * {@inheritDoc}
174
     */
175 1993
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
176
    {
177 1993
        $autoinc = '';
178 1993
        if (! empty($columnDef['autoincrement'])) {
179 1901
            $autoinc = ' GENERATED BY DEFAULT AS IDENTITY';
180
        }
181
182 1993
        return $autoinc;
183
    }
184
185
    /**
186
     * {@inheritdoc}
187
     */
188 1044
    public function getBitAndComparisonExpression($value1, $value2)
189
    {
190 1044
        return 'BITAND(' . $value1 . ', ' . $value2 . ')';
191
    }
192
193
    /**
194
     * {@inheritdoc}
195
     */
196 230
    public function getBitOrComparisonExpression($value1, $value2)
197
    {
198 230
        return 'BITOR(' . $value1 . ', ' . $value2 . ')';
199
    }
200
201
    /**
202
     * {@inheritdoc}
203
     */
204 1816
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
205
    {
206
        switch ($unit) {
207 1816
            case DateIntervalUnit::WEEK:
208 1816
                $interval *= 7;
209 1816
                $unit      = DateIntervalUnit::DAY;
210 1816
                break;
211
212 1816
            case DateIntervalUnit::QUARTER:
213 1816
                $interval *= 3;
214 1816
                $unit      = DateIntervalUnit::MONTH;
215 1816
                break;
216
        }
217
218 1816
        return $date . ' ' . $operator . ' ' . $interval . ' ' . $unit;
219
    }
220
221
    /**
222
     * {@inheritdoc}
223
     */
224 1798
    public function getDateDiffExpression($date1, $date2)
225
    {
226 1798
        return 'DAYS(' . $date1 . ') - DAYS(' . $date2 . ')';
227
    }
228
229
    /**
230
     * {@inheritDoc}
231
     */
232 1905
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
233
    {
234 1905
        if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] === true) {
235 1749
            return 'TIMESTAMP(0) WITH DEFAULT';
236
        }
237
238 1905
        return 'TIMESTAMP(0)';
239
    }
240
241
    /**
242
     * {@inheritDoc}
243
     */
244 1884
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
245
    {
246 1884
        return 'DATE';
247
    }
248
249
    /**
250
     * {@inheritDoc}
251
     */
252 1883
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
253
    {
254 1883
        return 'TIME';
255
    }
256
257
    /**
258
     * {@inheritdoc}
259
     */
260 1857
    public function getTruncateTableSQL($tableName, $cascade = false)
261
    {
262 1857
        $tableIdentifier = new Identifier($tableName);
263
264 1857
        return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this) . ' IMMEDIATE';
265
    }
266
267
    /**
268
     * This code fragment is originally from the Zend_Db_Adapter_Db2 class, but has been edited.
269
     *
270
     * @param string $table
271
     * @param string $database
272
     *
273
     * @return string
274
     */
275 1379
    public function getListTableColumnsSQL($table, $database = null)
276
    {
277 1379
        $table = $this->quoteStringLiteral($table);
278
279
        // We do the funky subquery and join syscat.columns.default this crazy way because
280
        // as of db2 v10, the column is CLOB(64k) and the distinct operator won't allow a CLOB,
281
        // it wants shorter stuff like a varchar.
282
        return "
283
        SELECT
284
          cols.default,
285
          subq.*
286
        FROM (
287
               SELECT DISTINCT
288
                 c.tabschema,
289
                 c.tabname,
290
                 c.colname,
291
                 c.colno,
292
                 c.typename,
293
                 c.nulls,
294
                 c.length,
295
                 c.scale,
296
                 c.identity,
297
                 tc.type AS tabconsttype,
298
                 c.remarks AS comment,
299
                 k.colseq,
300
                 CASE
301
                 WHEN c.generated = 'D' THEN 1
302
                 ELSE 0
303
                 END     AS autoincrement
304
               FROM syscat.columns c
305
                 LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc
306
                     ON (k.tabschema = tc.tabschema
307
                         AND k.tabname = tc.tabname
308
                         AND tc.type = 'P'))
309
                   ON (c.tabschema = k.tabschema
310
                       AND c.tabname = k.tabname
311
                       AND c.colname = k.colname)
312 1379
               WHERE UPPER(c.tabname) = UPPER(" . $table . ')
313
               ORDER BY c.colno
314
             ) subq
315
          JOIN syscat.columns cols
316
            ON subq.tabschema = cols.tabschema
317
               AND subq.tabname = cols.tabname
318
               AND subq.colno = cols.colno
319
        ORDER BY subq.colno
320
        ';
321
    }
322
323
    /**
324
     * {@inheritDoc}
325
     */
326 243
    public function getListTablesSQL()
327
    {
328 243
        return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = 'T'";
329
    }
330
331
    /**
332
     * {@inheritDoc}
333
     */
334 175
    public function getListViewsSQL($database)
335
    {
336 175
        return 'SELECT NAME, TEXT FROM SYSIBM.SYSVIEWS';
337
    }
338
339
    /**
340
     * {@inheritDoc}
341
     */
342 191
    public function getListTableIndexesSQL($table, $currentDatabase = null)
343
    {
344 191
        $table = $this->quoteStringLiteral($table);
345
346
        return "SELECT   idx.INDNAME AS key_name,
347
                         idxcol.COLNAME AS column_name,
348
                         CASE
349
                             WHEN idx.UNIQUERULE = 'P' THEN 1
350
                             ELSE 0
351
                         END AS primary,
352
                         CASE
353
                             WHEN idx.UNIQUERULE = 'D' THEN 1
354
                             ELSE 0
355
                         END AS non_unique
356
                FROM     SYSCAT.INDEXES AS idx
357
                JOIN     SYSCAT.INDEXCOLUSE AS idxcol
358
                ON       idx.INDSCHEMA = idxcol.INDSCHEMA AND idx.INDNAME = idxcol.INDNAME
359 191
                WHERE    idx.TABNAME = UPPER(" . $table . ')
360
                ORDER BY idxcol.COLSEQ ASC';
361
    }
362
363
    /**
364
     * {@inheritDoc}
365
     */
366 1357
    public function getListTableForeignKeysSQL($table)
367
    {
368 1357
        $table = $this->quoteStringLiteral($table);
369
370
        return "SELECT   fkcol.COLNAME AS local_column,
371
                         fk.REFTABNAME AS foreign_table,
372
                         pkcol.COLNAME AS foreign_column,
373
                         fk.CONSTNAME AS index_name,
374
                         CASE
375
                             WHEN fk.UPDATERULE = 'R' THEN 'RESTRICT'
376
                             ELSE NULL
377
                         END AS on_update,
378
                         CASE
379
                             WHEN fk.DELETERULE = 'C' THEN 'CASCADE'
380
                             WHEN fk.DELETERULE = 'N' THEN 'SET NULL'
381
                             WHEN fk.DELETERULE = 'R' THEN 'RESTRICT'
382
                             ELSE NULL
383
                         END AS on_delete
384
                FROM     SYSCAT.REFERENCES AS fk
385
                JOIN     SYSCAT.KEYCOLUSE AS fkcol
386
                ON       fk.CONSTNAME = fkcol.CONSTNAME
387
                AND      fk.TABSCHEMA = fkcol.TABSCHEMA
388
                AND      fk.TABNAME = fkcol.TABNAME
389
                JOIN     SYSCAT.KEYCOLUSE AS pkcol
390
                ON       fk.REFKEYNAME = pkcol.CONSTNAME
391
                AND      fk.REFTABSCHEMA = pkcol.TABSCHEMA
392
                AND      fk.REFTABNAME = pkcol.TABNAME
393 1357
                WHERE    fk.TABNAME = UPPER(" . $table . ')
394
                ORDER BY fkcol.COLSEQ ASC';
395
    }
396
397
    /**
398
     * {@inheritDoc}
399
     */
400 1804
    public function getCreateViewSQL($name, $sql)
401
    {
402 1804
        return 'CREATE VIEW ' . $name . ' AS ' . $sql;
403
    }
404
405
    /**
406
     * {@inheritDoc}
407
     */
408 1804
    public function getDropViewSQL($name)
409
    {
410 1804
        return 'DROP VIEW ' . $name;
411
    }
412
413
    /**
414
     * {@inheritDoc}
415
     */
416 1703
    public function getCreateDatabaseSQL($database)
417
    {
418 1703
        return 'CREATE DATABASE ' . $database;
419
    }
420
421
    /**
422
     * {@inheritDoc}
423
     */
424 1703
    public function getDropDatabaseSQL($database)
425
    {
426 1703
        return 'DROP DATABASE ' . $database;
427
    }
428
429
    /**
430
     * {@inheritDoc}
431
     */
432 244
    public function supportsCreateDropDatabase()
433
    {
434 244
        return false;
435
    }
436
437
    /**
438
     * {@inheritDoc}
439
     */
440 1
    public function supportsReleaseSavepoints()
441
    {
442 1
        return false;
443
    }
444
445
    /**
446
     * {@inheritdoc}
447
     */
448 1994
    public function supportsCommentOnStatement()
449
    {
450 1994
        return true;
451
    }
452
453
    /**
454
     * {@inheritDoc}
455
     */
456 1797
    public function getCurrentDateSQL()
457
    {
458 1797
        return 'CURRENT DATE';
459
    }
460
461
    /**
462
     * {@inheritDoc}
463
     */
464 1795
    public function getCurrentTimeSQL()
465
    {
466 1795
        return 'CURRENT TIME';
467
    }
468
469
    /**
470
     * {@inheritDoc}
471
     */
472 1795
    public function getCurrentTimestampSQL()
473
    {
474 1795
        return 'CURRENT TIMESTAMP';
475
    }
476
477
    /**
478
     * {@inheritDoc}
479
     */
480 438
    public function getIndexDeclarationSQL($name, Index $index)
481
    {
482
        // Index declaration in statements like CREATE TABLE is not supported.
483 438
        throw DBALException::notSupported(__METHOD__);
484
    }
485
486
    /**
487
     * {@inheritDoc}
488
     */
489 1993
    protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
490
    {
491 1993
        $indexes = [];
492 1993
        if (isset($options['indexes'])) {
493 1993
            $indexes = $options['indexes'];
494
        }
495
496 1993
        $options['indexes'] = [];
497
498 1993
        $sqls = parent::_getCreateTableSQL($tableName, $columns, $options);
499
500 1993
        foreach ($indexes as $definition) {
501 1925
            $sqls[] = $this->getCreateIndexSQL($definition, $tableName);
502
        }
503
504 1993
        return $sqls;
505
    }
506
507
    /**
508
     * {@inheritDoc}
509
     */
510 1630
    public function getAlterTableSQL(TableDiff $diff)
511
    {
512 1630
        $sql         = [];
513 1630
        $columnSql   = [];
514 1630
        $commentsSQL = [];
515
516 1630
        $queryParts = [];
517 1630
        foreach ($diff->addedColumns as $column) {
518 972
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
519
                continue;
520
            }
521
522 972
            $columnDef = $column->toArray();
523 972
            $queryPart = 'ADD COLUMN ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef);
524
525
            // Adding non-nullable columns to a table requires a default value to be specified.
526 972
            if (! empty($columnDef['notnull']) &&
527 972
                ! isset($columnDef['default']) &&
528 972
                empty($columnDef['autoincrement'])
529
            ) {
530 927
                $queryPart .= ' WITH DEFAULT';
531
            }
532
533 972
            $queryParts[] = $queryPart;
534
535 972
            $comment = $this->getColumnComment($column);
536
537 972
            if ($comment === null || $comment === '') {
538 971
                continue;
539
            }
540
541 737
            $commentsSQL[] = $this->getCommentOnColumnSQL(
542 737
                $diff->getName($this)->getQuotedName($this),
543 737
                $column->getQuotedName($this),
544
                $comment
545
            );
546
        }
547
548 1630
        foreach ($diff->removedColumns as $column) {
549 971
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
550
                continue;
551
            }
552
553 971
            $queryParts[] =  'DROP COLUMN ' . $column->getQuotedName($this);
554
        }
555
556 1630
        foreach ($diff->changedColumns as $columnDiff) {
557 1617
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
558
                continue;
559
            }
560
561 1617
            if ($columnDiff->hasChanged('comment')) {
562 875
                $commentsSQL[] = $this->getCommentOnColumnSQL(
563 875
                    $diff->getName($this)->getQuotedName($this),
564 875
                    $columnDiff->column->getQuotedName($this),
565 875
                    $this->getColumnComment($columnDiff->column)
566
                );
567
568 875
                if (count($columnDiff->changedProperties) === 1) {
569 875
                    continue;
570
                }
571
            }
572
573 1613
            $this->gatherAlterColumnSQL($diff->getName($this), $columnDiff, $sql, $queryParts);
574
        }
575
576 1630
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
577 924
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
578
                continue;
579
            }
580
581 924
            $oldColumnName = new Identifier($oldColumnName);
582
583 924
            $queryParts[] =  'RENAME COLUMN ' . $oldColumnName->getQuotedName($this) .
584 924
                ' TO ' . $column->getQuotedName($this);
585
        }
586
587 1630
        $tableSql = [];
588
589 1630
        if (! $this->onSchemaAlterTable($diff, $tableSql)) {
590 1630
            if (count($queryParts) > 0) {
591 1624
                $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(' ', $queryParts);
592
            }
593
594
            // Some table alteration operations require a table reorganization.
595 1630
            if (! empty($diff->removedColumns) || ! empty($diff->changedColumns)) {
596 1623
                $sql[] = "CALL SYSPROC.ADMIN_CMD ('REORG TABLE " . $diff->getName($this)->getQuotedName($this) . "')";
597
            }
598
599 1630
            $sql = array_merge($sql, $commentsSQL);
600
601 1630
            $newName = $diff->getNewName();
602
603 1630
            if ($newName !== false) {
604 830
                $sql[] = sprintf(
605 2
                    'RENAME TABLE %s TO %s',
606 830
                    $diff->getName($this)->getQuotedName($this),
607 830
                    $newName->getQuotedName($this)
608
                );
609
            }
610
611 1630
            $sql = array_merge(
612 1630
                $this->getPreAlterTableIndexForeignKeySQL($diff),
613 1630
                $sql,
614 1630
                $this->getPostAlterTableIndexForeignKeySQL($diff)
615
            );
616
        }
617
618 1630
        return array_merge($sql, $tableSql, $columnSql);
619
    }
620
621
    /**
622
     * Gathers the table alteration SQL for a given column diff.
623
     *
624
     * @param Identifier $table      The table to gather the SQL for.
625
     * @param ColumnDiff $columnDiff The column diff to evaluate.
626
     * @param string[]   $sql        The sequence of table alteration statements to fill.
627
     * @param mixed[]    $queryParts The sequence of column alteration clauses to fill.
628
     */
629 1613
    private function gatherAlterColumnSQL(Identifier $table, ColumnDiff $columnDiff, array &$sql, array &$queryParts) : void
630
    {
631 1613
        $alterColumnClauses = $this->getAlterColumnClausesSQL($columnDiff);
632
633 1613
        if (empty($alterColumnClauses)) {
634 1290
            return;
635
        }
636
637
        // If we have a single column alteration, we can append the clause to the main query.
638 1611
        if (count($alterColumnClauses) === 1) {
639 1610
            $queryParts[] = current($alterColumnClauses);
640
641 1610
            return;
642
        }
643
644
        // We have multiple alterations for the same column,
645
        // so we need to trigger a complete ALTER TABLE statement
646
        // for each ALTER COLUMN clause.
647 829
        foreach ($alterColumnClauses as $alterColumnClause) {
648 829
            $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ' . $alterColumnClause;
649
        }
650 829
    }
651
652
    /**
653
     * Returns the ALTER COLUMN SQL clauses for altering a column described by the given column diff.
654
     *
655
     * @param ColumnDiff $columnDiff The column diff to evaluate.
656
     *
657
     * @return string[]
658
     */
659 1613
    private function getAlterColumnClausesSQL(ColumnDiff $columnDiff)
660
    {
661 1613
        $column = $columnDiff->column->toArray();
662
663 1613
        $alterClause = 'ALTER COLUMN ' . $columnDiff->column->getQuotedName($this);
664
665 1613
        if ($column['columnDefinition']) {
666 1496
            return [$alterClause . ' ' . $column['columnDefinition']];
667
        }
668
669 1590
        $clauses = [];
670
671 1590
        if ($columnDiff->hasChanged('type') ||
672 1587
            $columnDiff->hasChanged('length') ||
673 1586
            $columnDiff->hasChanged('precision') ||
674 1585
            $columnDiff->hasChanged('scale') ||
675 1590
            $columnDiff->hasChanged('fixed')
676
        ) {
677 1583
            $clauses[] = $alterClause . ' SET DATA TYPE ' . $column['type']->getSQLDeclaration($column, $this);
678
        }
679
680 1590
        if ($columnDiff->hasChanged('notnull')) {
681 1361
            $clauses[] = $column['notnull'] ? $alterClause . ' SET NOT NULL' : $alterClause . ' DROP NOT NULL';
682
        }
683
684 1590
        if ($columnDiff->hasChanged('default')) {
685 1422
            if (isset($column['default'])) {
686 1421
                $defaultClause = $this->getDefaultValueDeclarationSQL($column);
687
688 1421
                if ($defaultClause) {
689 1421
                    $clauses[] = $alterClause . ' SET' . $defaultClause;
690
                }
691
            } else {
692 1375
                $clauses[] = $alterClause . ' DROP DEFAULT';
693
            }
694
        }
695
696 1590
        return $clauses;
697
    }
698
699
    /**
700
     * {@inheritDoc}
701
     */
702 1630
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
703
    {
704 1630
        $sql   = [];
705 1630
        $table = $diff->getName($this)->getQuotedName($this);
706
707 1630
        foreach ($diff->removedIndexes as $remKey => $remIndex) {
708 176
            foreach ($diff->addedIndexes as $addKey => $addIndex) {
709
                if ($remIndex->getColumns() !== $addIndex->getColumns()) {
710
                    continue;
711
                }
712
713
                if ($remIndex->isPrimary()) {
714
                    $sql[] = 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY';
715
                } elseif ($remIndex->isUnique()) {
716
                    $sql[] = 'ALTER TABLE ' . $table . ' DROP UNIQUE ' . $remIndex->getQuotedName($this);
717
                } else {
718
                    $sql[] = $this->getDropIndexSQL($remIndex, $table);
719
                }
720
721
                $sql[] = $this->getCreateIndexSQL($addIndex, $table);
722
723
                unset($diff->removedIndexes[$remKey], $diff->addedIndexes[$addKey]);
724
725
                break;
726
            }
727
        }
728
729 1630
        $sql = array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff));
730
731 1630
        return $sql;
732
    }
733
734
    /**
735
     * {@inheritdoc}
736
     */
737 489
    protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
738
    {
739 489
        if (strpos($tableName, '.') !== false) {
740 255
            [$schema]     = explode('.', $tableName);
0 ignored issues
show
Bug introduced by
The variable $schema does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
741 255
            $oldIndexName = $schema . '.' . $oldIndexName;
742
        }
743
744 489
        return ['RENAME INDEX ' . $oldIndexName . ' TO ' . $index->getQuotedName($this)];
745
    }
746
747
    /**
748
     * {@inheritDoc}
749
     */
750 2003
    public function getDefaultValueDeclarationSQL($field)
751
    {
752 2003
        if (! empty($field['autoincrement'])) {
753 1461
            return '';
754
        }
755
756 2002
        if (isset($field['version']) && $field['version']) {
757
            if ((string) $field['type'] !== 'DateTime') {
758
                $field['default'] = '1';
759
            }
760
        }
761
762 2002
        return parent::getDefaultValueDeclarationSQL($field);
763
    }
764
765
    /**
766
     * {@inheritDoc}
767
     */
768 83
    public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName)
769
    {
770 83
        return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (DEFAULT)';
771
    }
772
773
    /**
774
     * {@inheritDoc}
775
     */
776 1744
    public function getCreateTemporaryTableSnippetSQL()
777
    {
778 1744
        return 'DECLARE GLOBAL TEMPORARY TABLE';
779
    }
780
781
    /**
782
     * {@inheritDoc}
783
     */
784 115
    public function getTemporaryTableName($tableName)
785
    {
786 115
        return 'SESSION.' . $tableName;
787
    }
788
789
    /**
790
     * {@inheritDoc}
791
     */
792 1791
    protected function doModifyLimitQuery($query, $limit, $offset = null)
793
    {
794 1791
        $where = [];
795
796 1791
        if ($offset > 0) {
797 1790
            $where[] = sprintf('db22.DC_ROWNUM >= %d', $offset + 1);
798
        }
799
800 1791
        if ($limit !== null) {
801 1790
            $where[] = sprintf('db22.DC_ROWNUM <= %d', $offset + $limit);
802
        }
803
804 1791
        if (empty($where)) {
805 1785
            return $query;
806
        }
807
808
        // Todo OVER() needs ORDER BY data!
809 1790
        return sprintf(
810 1
            'SELECT db22.* FROM (SELECT db21.*, ROW_NUMBER() OVER() AS DC_ROWNUM FROM (%s) db21) db22 WHERE %s',
811 1790
            $query,
812 1790
            implode(' AND ', $where)
813
        );
814
    }
815
816
    /**
817
     * {@inheritDoc}
818
     */
819 1815
    public function getLocateExpression($str, $substr, $startPos = false)
820
    {
821 1815
        if ($startPos === false) {
822 1815
            return 'LOCATE(' . $substr . ', ' . $str . ')';
823
        }
824
825 1815
        return 'LOCATE(' . $substr . ', ' . $str . ', ' . $startPos . ')';
826
    }
827
828
    /**
829
     * {@inheritDoc}
830
     */
831 1657
    public function getSubstringExpression($value, $from, $length = null)
832
    {
833 1657
        if ($length === null) {
834 1657
            return 'SUBSTR(' . $value . ', ' . $from . ')';
835
        }
836
837 1657
        return 'SUBSTR(' . $value . ', ' . $from . ', ' . $length . ')';
838
    }
839
840
    /**
841
     * {@inheritDoc}
842
     */
843 175
    public function supportsIdentityColumns()
844
    {
845 175
        return true;
846
    }
847
848
    /**
849
     * {@inheritDoc}
850
     */
851 1628
    public function prefersIdentityColumns()
852
    {
853 1628
        return true;
854
    }
855
856
    /**
857
     * {@inheritDoc}
858
     *
859
     * DB2 returns all column names in SQL result sets in uppercase.
860
     */
861 1588
    public function getSQLResultCasing($column)
862
    {
863 1588
        return strtoupper($column);
864
    }
865
866
    /**
867
     * {@inheritDoc}
868
     */
869 1702
    public function getForUpdateSQL()
870
    {
871 1702
        return ' WITH RR USE AND KEEP UPDATE LOCKS';
872
    }
873
874
    /**
875
     * {@inheritDoc}
876
     */
877 235
    public function getDummySelectSQL()
878
    {
879 235
        $expression = func_num_args() > 0 ? func_get_arg(0) : '1';
880
881 235
        return sprintf('SELECT %s FROM sysibm.sysdummy1', $expression);
882
    }
883
884
    /**
885
     * {@inheritDoc}
886
     *
887
     * DB2 supports savepoints, but they work semantically different than on other vendor platforms.
888
     *
889
     * TODO: We have to investigate how to get DB2 up and running with savepoints.
890
     */
891 238
    public function supportsSavepoints()
892
    {
893 238
        return false;
894
    }
895
896
    /**
897
     * {@inheritDoc}
898
     */
899 2032
    protected function getReservedKeywordsClass()
900
    {
901 2032
        return Keywords\DB2Keywords::class;
902
    }
903
904 190
    public function getListTableCommentsSQL(string $table) : string
905
    {
906 190
        return sprintf(
907
            <<<'SQL'
908
SELECT REMARKS
909
  FROM SYSIBM.SYSTABLES
910
  WHERE NAME = UPPER( %s )
911
SQL
912
            ,
913 190
            $this->quoteStringLiteral($table)
914
        );
915
    }
916
}
917