Failed Conditions
Pull Request — master (#3973)
by Grégoire
03:04
created

DB2Platform::getAlterColumnClausesSQL()   C

Complexity

Conditions 12
Paths 25

Size

Total Lines 38
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 12

Importance

Changes 0
Metric Value
eloc 21
dl 0
loc 38
ccs 21
cts 21
cp 1
rs 6.9666
c 0
b 0
f 0
cc 12
nc 25
nop 1
crap 12

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