DB2Platform::getAlterTableSQL()   F
last analyzed

Complexity

Conditions 21
Paths 2430

Size

Total Lines 102

Duplication

Lines 24
Ratio 23.53 %

Importance

Changes 0
Metric Value
cc 21
nc 2430
nop 1
dl 24
loc 102
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\DBAL\Platforms;
21
22
use Doctrine\DBAL\DBALException;
23
use Doctrine\DBAL\Schema\ColumnDiff;
24
use Doctrine\DBAL\Schema\Identifier;
25
use Doctrine\DBAL\Schema\Index;
26
use Doctrine\DBAL\Schema\Table;
27
use Doctrine\DBAL\Schema\TableDiff;
28
use Doctrine\DBAL\Types\Type;
29
use function array_merge;
30
use function count;
31
use function current;
32
use function explode;
33
use function func_get_arg;
34
use function func_num_args;
35
use function implode;
36
use function sprintf;
37
use function strpos;
38
use function strtoupper;
39
40
class DB2Platform extends AbstractPlatform
41
{
42
    /**
43
     * {@inheritdoc}
44
     */
45
    public function getCharMaxLength() : int
46
    {
47
        return 254;
48
    }
49
50
    /**
51
     * {@inheritdoc}
52
     */
53
    public function getBinaryMaxLength()
54
    {
55
        return 32704;
56
    }
57
58
    /**
59
     * {@inheritdoc}
60
     */
61
    public function getBinaryDefaultLength()
62
    {
63
        return 1;
64
    }
65
66
    /**
67
     * {@inheritDoc}
68
     */
69
    public function getVarcharTypeDeclarationSQL(array $field)
70
    {
71
        // for IBM DB2, the CHAR max length is less than VARCHAR default length
72
        if (! isset($field['length']) && ! empty($field['fixed'])) {
73
            $field['length'] = $this->getCharMaxLength();
74
        }
75
76
        return parent::getVarcharTypeDeclarationSQL($field);
77
    }
78
79
    /**
80
     * {@inheritDoc}
81
     */
82
    public function getBlobTypeDeclarationSQL(array $field)
83
    {
84
        // todo blob(n) with $field['length'];
85
        return 'BLOB(1M)';
86
    }
87
88
    /**
89
     * {@inheritDoc}
90
     */
91
    public function initializeDoctrineTypeMappings()
92
    {
93
        $this->doctrineTypeMapping = [
94
            'smallint'      => 'smallint',
95
            'bigint'        => 'bigint',
96
            'integer'       => 'integer',
97
            'time'          => 'time',
98
            'date'          => 'date',
99
            'varchar'       => 'string',
100
            'character'     => 'string',
101
            'varbinary'     => 'binary',
102
            'binary'        => 'binary',
103
            'clob'          => 'text',
104
            'blob'          => 'blob',
105
            'decimal'       => 'decimal',
106
            'double'        => 'float',
107
            'real'          => 'float',
108
            'timestamp'     => 'datetime',
109
        ];
110
    }
111
112
    /**
113
     * {@inheritdoc}
114
     */
115
    public function isCommentedDoctrineType(Type $doctrineType)
116
    {
117
        if ($doctrineType->getName() === Type::BOOLEAN) {
118
            // We require a commented boolean type in order to distinguish between boolean and smallint
119
            // as both (have to) map to the same native type.
120
            return true;
121
        }
122
123
        return parent::isCommentedDoctrineType($doctrineType);
124
    }
125
126
    /**
127
     * {@inheritDoc}
128
     */
129
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
130
    {
131
        return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(254)')
132
                : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)');
133
    }
134
135
    /**
136
     * {@inheritdoc}
137
     */
138
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
139
    {
140
        return $this->getVarcharTypeDeclarationSQLSnippet($length, $fixed) . ' FOR BIT DATA';
141
    }
142
143
    /**
144
     * {@inheritDoc}
145
     */
146
    public function getClobTypeDeclarationSQL(array $field)
147
    {
148
        // todo clob(n) with $field['length'];
149
        return 'CLOB(1M)';
150
    }
151
152
    /**
153
     * {@inheritDoc}
154
     */
155
    public function getName()
156
    {
157
        return 'db2';
158
    }
159
160
    /**
161
     * {@inheritDoc}
162
     */
163
    public function getBooleanTypeDeclarationSQL(array $columnDef)
164
    {
165
        return 'SMALLINT';
166
    }
167
168
    /**
169
     * {@inheritDoc}
170
     */
171
    public function getIntegerTypeDeclarationSQL(array $columnDef)
172
    {
173
        return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
174
    }
175
176
    /**
177
     * {@inheritDoc}
178
     */
179
    public function getBigIntTypeDeclarationSQL(array $columnDef)
180
    {
181
        return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
182
    }
183
184
    /**
185
     * {@inheritDoc}
186
     */
187
    public function getSmallIntTypeDeclarationSQL(array $columnDef)
188
    {
189
        return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
190
    }
191
192
    /**
193
     * {@inheritDoc}
194
     */
195
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
196
    {
197
        $autoinc = '';
198
        if ( ! empty($columnDef['autoincrement'])) {
199
            $autoinc = ' GENERATED BY DEFAULT AS IDENTITY';
200
        }
201
202
        return $autoinc;
203
    }
204
205
    /**
206
     * {@inheritdoc}
207
     */
208
    public function getBitAndComparisonExpression($value1, $value2)
209
    {
210
        return 'BITAND(' . $value1 . ', ' . $value2 . ')';
211
    }
212
213
    /**
214
     * {@inheritdoc}
215
     */
216
    public function getBitOrComparisonExpression($value1, $value2)
217
    {
218
        return 'BITOR(' . $value1 . ', ' . $value2 . ')';
219
    }
220
221
    /**
222
     * {@inheritdoc}
223
     */
224
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
225
    {
226 View Code Duplication
        switch ($unit) {
227
            case DateIntervalUnit::WEEK:
228
                $interval *= 7;
229
                $unit      = DateIntervalUnit::DAY;
230
                break;
231
232
            case DateIntervalUnit::QUARTER:
233
                $interval *= 3;
234
                $unit      = DateIntervalUnit::MONTH;
235
                break;
236
        }
237
238
        return $date . ' ' . $operator . ' ' . $interval . ' ' . $unit;
239
    }
240
241
    /**
242
     * {@inheritdoc}
243
     */
244
    public function getDateDiffExpression($date1, $date2)
245
    {
246
        return 'DAYS(' . $date1 . ') - DAYS(' . $date2 . ')';
247
    }
248
249
    /**
250
     * {@inheritDoc}
251
     */
252
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
253
    {
254
        if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) {
255
            return "TIMESTAMP(0) WITH DEFAULT";
256
        }
257
258
        return 'TIMESTAMP(0)';
259
    }
260
261
    /**
262
     * {@inheritDoc}
263
     */
264
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
265
    {
266
        return 'DATE';
267
    }
268
269
    /**
270
     * {@inheritDoc}
271
     */
272
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
273
    {
274
        return 'TIME';
275
    }
276
277
    /**
278
     * {@inheritdoc}
279
     */
280
    public function getTruncateTableSQL($tableName, $cascade = false)
281
    {
282
        $tableIdentifier = new Identifier($tableName);
283
284
        return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this) . ' IMMEDIATE';
285
    }
286
287
    /**
288
     * This code fragment is originally from the Zend_Db_Adapter_Db2 class, but has been edited.
289
     *
290
     * @license New BSD License
291
     *
292
     * @param string $table
293
     * @param string $database
0 ignored issues
show
Documentation introduced by
Should the type for parameter $database not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
294
     *
295
     * @return string
296
     */
297
    public function getListTableColumnsSQL($table, $database = null)
298
    {
299
        $table = $this->quoteStringLiteral($table);
300
301
        // We do the funky subquery and join syscat.columns.default this crazy way because
302
        // as of db2 v10, the column is CLOB(64k) and the distinct operator won't allow a CLOB,
303
        // it wants shorter stuff like a varchar.
304
        return "
305
        SELECT
306
          cols.default,
307
          subq.*
308
        FROM (
309
               SELECT DISTINCT
310
                 c.tabschema,
311
                 c.tabname,
312
                 c.colname,
313
                 c.colno,
314
                 c.typename,
315
                 c.nulls,
316
                 c.length,
317
                 c.scale,
318
                 c.identity,
319
                 tc.type AS tabconsttype,
320
                 c.remarks AS comment,
321
                 k.colseq,
322
                 CASE
323
                 WHEN c.generated = 'D' THEN 1
324
                 ELSE 0
325
                 END     AS autoincrement
326
               FROM syscat.columns c
327
                 LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc
328
                     ON (k.tabschema = tc.tabschema
329
                         AND k.tabname = tc.tabname
330
                         AND tc.type = 'P'))
331
                   ON (c.tabschema = k.tabschema
332
                       AND c.tabname = k.tabname
333
                       AND c.colname = k.colname)
334
               WHERE UPPER(c.tabname) = UPPER(" . $table . ")
335
               ORDER BY c.colno
336
             ) subq
337
          JOIN syscat.columns cols
338
            ON subq.tabschema = cols.tabschema
339
               AND subq.tabname = cols.tabname
340
               AND subq.colno = cols.colno
341
        ORDER BY subq.colno
342
        ";
343
    }
344
345
    /**
346
     * {@inheritDoc}
347
     */
348
    public function getListTablesSQL()
349
    {
350
        return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = 'T'";
351
    }
352
353
    /**
354
     * {@inheritDoc}
355
     */
356
    public function getListViewsSQL($database)
357
    {
358
        return "SELECT NAME, TEXT FROM SYSIBM.SYSVIEWS";
359
    }
360
361
    /**
362
     * {@inheritDoc}
363
     */
364
    public function getListTableIndexesSQL($table, $currentDatabase = null)
365
    {
366
        $table = $this->quoteStringLiteral($table);
367
368
        return "SELECT   idx.INDNAME AS key_name,
369
                         idxcol.COLNAME AS column_name,
370
                         CASE
371
                             WHEN idx.UNIQUERULE = 'P' THEN 1
372
                             ELSE 0
373
                         END AS primary,
374
                         CASE
375
                             WHEN idx.UNIQUERULE = 'D' THEN 1
376
                             ELSE 0
377
                         END AS non_unique
378
                FROM     SYSCAT.INDEXES AS idx
379
                JOIN     SYSCAT.INDEXCOLUSE AS idxcol
380
                ON       idx.INDSCHEMA = idxcol.INDSCHEMA AND idx.INDNAME = idxcol.INDNAME
381
                WHERE    idx.TABNAME = UPPER(" . $table . ")
382
                ORDER BY idxcol.COLSEQ ASC";
383
    }
384
385
    /**
386
     * {@inheritDoc}
387
     */
388
    public function getListTableForeignKeysSQL($table)
389
    {
390
        $table = $this->quoteStringLiteral($table);
391
392
        return "SELECT   fkcol.COLNAME AS local_column,
393
                         fk.REFTABNAME AS foreign_table,
394
                         pkcol.COLNAME AS foreign_column,
395
                         fk.CONSTNAME AS index_name,
396
                         CASE
397
                             WHEN fk.UPDATERULE = 'R' THEN 'RESTRICT'
398
                             ELSE NULL
399
                         END AS on_update,
400
                         CASE
401
                             WHEN fk.DELETERULE = 'C' THEN 'CASCADE'
402
                             WHEN fk.DELETERULE = 'N' THEN 'SET NULL'
403
                             WHEN fk.DELETERULE = 'R' THEN 'RESTRICT'
404
                             ELSE NULL
405
                         END AS on_delete
406
                FROM     SYSCAT.REFERENCES AS fk
407
                JOIN     SYSCAT.KEYCOLUSE AS fkcol
408
                ON       fk.CONSTNAME = fkcol.CONSTNAME
409
                AND      fk.TABSCHEMA = fkcol.TABSCHEMA
410
                AND      fk.TABNAME = fkcol.TABNAME
411
                JOIN     SYSCAT.KEYCOLUSE AS pkcol
412
                ON       fk.REFKEYNAME = pkcol.CONSTNAME
413
                AND      fk.REFTABSCHEMA = pkcol.TABSCHEMA
414
                AND      fk.REFTABNAME = pkcol.TABNAME
415
                WHERE    fk.TABNAME = UPPER(" . $table . ")
416
                ORDER BY fkcol.COLSEQ ASC";
417
    }
418
419
    /**
420
     * {@inheritDoc}
421
     */
422
    public function getCreateViewSQL($name, $sql)
423
    {
424
        return "CREATE VIEW ".$name." AS ".$sql;
425
    }
426
427
    /**
428
     * {@inheritDoc}
429
     */
430
    public function getDropViewSQL($name)
431
    {
432
        return "DROP VIEW ".$name;
433
    }
434
435
    /**
436
     * {@inheritDoc}
437
     */
438
    public function getCreateDatabaseSQL($database)
439
    {
440
        return "CREATE DATABASE ".$database;
441
    }
442
443
    /**
444
     * {@inheritDoc}
445
     */
446
    public function getDropDatabaseSQL($database)
447
    {
448
        return "DROP DATABASE " . $database;
449
    }
450
451
    /**
452
     * {@inheritDoc}
453
     */
454
    public function supportsCreateDropDatabase()
455
    {
456
        return false;
457
    }
458
459
    /**
460
     * {@inheritDoc}
461
     */
462
    public function supportsReleaseSavepoints()
463
    {
464
        return false;
465
    }
466
467
    /**
468
     * {@inheritdoc}
469
     */
470
    public function supportsCommentOnStatement()
471
    {
472
        return true;
473
    }
474
475
    /**
476
     * {@inheritDoc}
477
     */
478
    public function getCurrentDateSQL()
479
    {
480
        return 'CURRENT DATE';
481
    }
482
483
    /**
484
     * {@inheritDoc}
485
     */
486
    public function getCurrentTimeSQL()
487
    {
488
        return 'CURRENT TIME';
489
    }
490
491
    /**
492
     * {@inheritDoc}
493
     */
494
    public function getCurrentTimestampSQL()
495
    {
496
        return "CURRENT TIMESTAMP";
497
    }
498
499
    /**
500
     * {@inheritDoc}
501
     */
502
    public function getIndexDeclarationSQL($name, Index $index)
503
    {
504
        // Index declaration in statements like CREATE TABLE is not supported.
505
        throw DBALException::notSupported(__METHOD__);
506
    }
507
508
    /**
509
     * {@inheritDoc}
510
     */
511
    protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
512
    {
513
        $indexes = [];
514
        if (isset($options['indexes'])) {
515
            $indexes = $options['indexes'];
516
        }
517
        $options['indexes'] = [];
518
519
        $sqls = parent::_getCreateTableSQL($tableName, $columns, $options);
520
521
        foreach ($indexes as $definition) {
522
            $sqls[] = $this->getCreateIndexSQL($definition, $tableName);
523
        }
524
        return $sqls;
525
    }
526
527
    /**
528
     * {@inheritDoc}
529
     */
530
    public function getAlterTableSQL(TableDiff $diff)
531
    {
532
        $sql = [];
533
        $columnSql = [];
534
        $commentsSQL = [];
535
536
        $queryParts = [];
537
        foreach ($diff->addedColumns as $column) {
538
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
539
                continue;
540
            }
541
542
            $columnDef = $column->toArray();
543
            $queryPart = 'ADD COLUMN ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef);
544
545
            // Adding non-nullable columns to a table requires a default value to be specified.
546
            if ( ! empty($columnDef['notnull']) &&
547
                ! isset($columnDef['default']) &&
548
                empty($columnDef['autoincrement'])
549
            ) {
550
                $queryPart .= ' WITH DEFAULT';
551
            }
552
553
            $queryParts[] = $queryPart;
554
555
            $comment = $this->getColumnComment($column);
556
557 View Code Duplication
            if (null !== $comment && '' !== $comment) {
558
                $commentsSQL[] = $this->getCommentOnColumnSQL(
559
                    $diff->getName($this)->getQuotedName($this),
560
                    $column->getQuotedName($this),
561
                    $comment
562
                );
563
            }
564
        }
565
566 View Code Duplication
        foreach ($diff->removedColumns as $column) {
567
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
568
                continue;
569
            }
570
571
            $queryParts[] =  'DROP COLUMN ' . $column->getQuotedName($this);
572
        }
573
574
        foreach ($diff->changedColumns as $columnDiff) {
575
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
576
                continue;
577
            }
578
579
            if ($columnDiff->hasChanged('comment')) {
580
                $commentsSQL[] = $this->getCommentOnColumnSQL(
581
                    $diff->getName($this)->getQuotedName($this),
582
                    $columnDiff->column->getQuotedName($this),
583
                    $this->getColumnComment($columnDiff->column)
584
                );
585
586
                if (count($columnDiff->changedProperties) === 1) {
587
                    continue;
588
                }
589
            }
590
591
            $this->gatherAlterColumnSQL($diff->fromTable, $columnDiff, $sql, $queryParts);
592
        }
593
594 View Code Duplication
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
595
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
596
                continue;
597
            }
598
599
            $oldColumnName = new Identifier($oldColumnName);
600
601
            $queryParts[] =  'RENAME COLUMN ' . $oldColumnName->getQuotedName($this) .
602
                ' TO ' . $column->getQuotedName($this);
603
        }
604
605
        $tableSql = [];
606
607
        if ( ! $this->onSchemaAlterTable($diff, $tableSql)) {
608
            if (count($queryParts) > 0) {
609
                $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(" ", $queryParts);
610
            }
611
612
            // Some table alteration operations require a table reorganization.
613
            if ( ! empty($diff->removedColumns) || ! empty($diff->changedColumns)) {
614
                $sql[] = "CALL SYSPROC.ADMIN_CMD ('REORG TABLE " . $diff->getName($this)->getQuotedName($this) . "')";
615
            }
616
617
            $sql = array_merge($sql, $commentsSQL);
618
619
            if ($diff->newName !== false) {
620
                $sql[] =  'RENAME TABLE ' . $diff->getName($this)->getQuotedName($this) . ' TO ' . $diff->getNewName()->getQuotedName($this);
621
            }
622
623
            $sql = array_merge(
624
                $this->getPreAlterTableIndexForeignKeySQL($diff),
625
                $sql,
626
                $this->getPostAlterTableIndexForeignKeySQL($diff)
627
            );
628
        }
629
630
        return array_merge($sql, $tableSql, $columnSql);
631
    }
632
633
    /**
634
     * Gathers the table alteration SQL for a given column diff.
635
     *
636
     * @param Table      $table      The table to gather the SQL for.
637
     * @param ColumnDiff $columnDiff The column diff to evaluate.
638
     * @param array      $sql        The sequence of table alteration statements to fill.
639
     * @param array      $queryParts The sequence of column alteration clauses to fill.
640
     */
641
    private function gatherAlterColumnSQL(Table $table, ColumnDiff $columnDiff, array &$sql, array &$queryParts)
642
    {
643
        $alterColumnClauses = $this->getAlterColumnClausesSQL($columnDiff);
644
645
        if (empty($alterColumnClauses)) {
646
            return;
647
        }
648
649
        // If we have a single column alteration, we can append the clause to the main query.
650
        if (count($alterColumnClauses) === 1) {
651
            $queryParts[] = current($alterColumnClauses);
652
653
            return;
654
        }
655
656
        // We have multiple alterations for the same column,
657
        // so we need to trigger a complete ALTER TABLE statement
658
        // for each ALTER COLUMN clause.
659
        foreach ($alterColumnClauses as $alterColumnClause) {
660
            $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ' . $alterColumnClause;
661
        }
662
    }
663
664
    /**
665
     * Returns the ALTER COLUMN SQL clauses for altering a column described by the given column diff.
666
     *
667
     * @param ColumnDiff $columnDiff The column diff to evaluate.
668
     *
669
     * @return array
670
     */
671
    private function getAlterColumnClausesSQL(ColumnDiff $columnDiff)
672
    {
673
        $column = $columnDiff->column->toArray();
674
675
        $alterClause = 'ALTER COLUMN ' . $columnDiff->column->getQuotedName($this);
676
677
        if ($column['columnDefinition']) {
678
            return [$alterClause . ' ' . $column['columnDefinition']];
679
        }
680
681
        $clauses = [];
682
683
        if ($columnDiff->hasChanged('type') ||
684
            $columnDiff->hasChanged('length') ||
685
            $columnDiff->hasChanged('precision') ||
686
            $columnDiff->hasChanged('scale') ||
687
            $columnDiff->hasChanged('fixed')
688
        ) {
689
            $clauses[] = $alterClause . ' SET DATA TYPE ' . $column['type']->getSQLDeclaration($column, $this);
690
        }
691
692
        if ($columnDiff->hasChanged('notnull')) {
693
            $clauses[] = $column['notnull'] ? $alterClause . ' SET NOT NULL' : $alterClause . ' DROP NOT NULL';
694
        }
695
696
        if ($columnDiff->hasChanged('default')) {
697
            if (isset($column['default'])) {
698
                $defaultClause = $this->getDefaultValueDeclarationSQL($column);
699
700
                if ($defaultClause) {
701
                    $clauses[] = $alterClause . ' SET' . $defaultClause;
702
                }
703
            } else {
704
                $clauses[] = $alterClause . ' DROP DEFAULT';
705
            }
706
        }
707
708
        return $clauses;
709
    }
710
711
    /**
712
     * {@inheritDoc}
713
     */
714
    protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
715
    {
716
        $sql = [];
717
        $table = $diff->getName($this)->getQuotedName($this);
718
719
        foreach ($diff->removedIndexes as $remKey => $remIndex) {
720
            foreach ($diff->addedIndexes as $addKey => $addIndex) {
721
                if ($remIndex->getColumns() == $addIndex->getColumns()) {
722
                    if ($remIndex->isPrimary()) {
723
                        $sql[] = 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY';
724
                    } elseif ($remIndex->isUnique()) {
725
                        $sql[] = 'ALTER TABLE ' . $table . ' DROP UNIQUE ' . $remIndex->getQuotedName($this);
726
                    } else {
727
                        $sql[] = $this->getDropIndexSQL($remIndex, $table);
728
                    }
729
730
                    $sql[] = $this->getCreateIndexSQL($addIndex, $table);
731
732
                    unset($diff->removedIndexes[$remKey], $diff->addedIndexes[$addKey]);
733
734
                    break;
735
                }
736
            }
737
        }
738
739
        $sql = array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff));
740
741
        return $sql;
742
    }
743
744
    /**
745
     * {@inheritdoc}
746
     */
747 View Code Duplication
    protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
748
    {
749
        if (strpos($tableName, '.') !== false) {
750
            list($schema) = explode('.', $tableName);
751
            $oldIndexName = $schema . '.' . $oldIndexName;
752
        }
753
754
        return ['RENAME INDEX ' . $oldIndexName . ' TO ' . $index->getQuotedName($this)];
755
    }
756
757
    /**
758
     * {@inheritDoc}
759
     */
760
    public function getDefaultValueDeclarationSQL($field)
761
    {
762
        if ( ! empty($field['autoincrement'])) {
763
            return '';
764
        }
765
766
        if (isset($field['version']) && $field['version']) {
767
            if ((string) $field['type'] != "DateTime") {
768
                $field['default'] = "1";
769
            }
770
        }
771
772
        return parent::getDefaultValueDeclarationSQL($field);
773
    }
774
775
    /**
776
     * {@inheritDoc}
777
     */
778
    public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName)
779
    {
780
        return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (DEFAULT)';
781
    }
782
783
    /**
784
     * {@inheritDoc}
785
     */
786
    public function getCreateTemporaryTableSnippetSQL()
787
    {
788
        return "DECLARE GLOBAL TEMPORARY TABLE";
789
    }
790
791
    /**
792
     * {@inheritDoc}
793
     */
794
    public function getTemporaryTableName($tableName)
795
    {
796
        return "SESSION." . $tableName;
797
    }
798
799
    /**
800
     * {@inheritDoc}
801
     */
802
    protected function doModifyLimitQuery($query, $limit, $offset = null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
803
    {
804
        $where = [];
805
806
        if ($offset > 0) {
807
            $where[] = sprintf('db22.DC_ROWNUM >= %d', $offset + 1);
808
        }
809
810
        if ($limit !== null) {
811
            $where[] = sprintf('db22.DC_ROWNUM <= %d', $offset + $limit);
812
        }
813
814
        if (empty($where)) {
815
            return $query;
816
        }
817
818
        // Todo OVER() needs ORDER BY data!
819
        return sprintf(
820
            'SELECT db22.* FROM (SELECT db21.*, ROW_NUMBER() OVER() AS DC_ROWNUM FROM (%s) db21) db22 WHERE %s',
821
            $query,
822
            implode(' AND ', $where)
823
        );
824
    }
825
826
    /**
827
     * {@inheritDoc}
828
     */
829 View Code Duplication
    public function getLocateExpression($str, $substr, $startPos = false)
830
    {
831
        if ($startPos == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
832
            return 'LOCATE(' . $substr . ', ' . $str . ')';
833
        }
834
835
        return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')';
836
    }
837
838
    /**
839
     * {@inheritDoc}
840
     */
841 View Code Duplication
    public function getSubstringExpression($value, $from, $length = null)
842
    {
843
        if ($length === null) {
844
            return 'SUBSTR(' . $value . ', ' . $from . ')';
845
        }
846
847
        return 'SUBSTR(' . $value . ', ' . $from . ', ' . $length . ')';
848
    }
849
850
    /**
851
     * {@inheritDoc}
852
     */
853
    public function supportsIdentityColumns()
854
    {
855
        return true;
856
    }
857
858
    /**
859
     * {@inheritDoc}
860
     */
861
    public function prefersIdentityColumns()
862
    {
863
        return true;
864
    }
865
866
    /**
867
     * {@inheritDoc}
868
     *
869
     * DB2 returns all column names in SQL result sets in uppercase.
870
     */
871
    public function getSQLResultCasing($column)
872
    {
873
        return strtoupper($column);
874
    }
875
876
    /**
877
     * {@inheritDoc}
878
     */
879
    public function getForUpdateSQL()
880
    {
881
        return ' WITH RR USE AND KEEP UPDATE LOCKS';
882
    }
883
884
    /**
885
     * {@inheritDoc}
886
     */
887
    public function getDummySelectSQL()
888
    {
889
        $expression = func_num_args() > 0 ? func_get_arg(0) : '1';
890
891
        return sprintf('SELECT %s FROM sysibm.sysdummy1', $expression);
892
    }
893
894
    /**
895
     * {@inheritDoc}
896
     *
897
     * DB2 supports savepoints, but they work semantically different than on other vendor platforms.
898
     *
899
     * TODO: We have to investigate how to get DB2 up and running with savepoints.
900
     */
901
    public function supportsSavepoints()
902
    {
903
        return false;
904
    }
905
906
    /**
907
     * {@inheritDoc}
908
     */
909
    protected function getReservedKeywordsClass()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
910
    {
911
        return Keywords\DB2Keywords::class;
912
    }
913
}
914