Passed
Pull Request — master (#3135)
by Michael
17:53
created

OraclePlatform::getAlterTableSQL()   F

Complexity

Conditions 22
Paths 7776

Size

Total Lines 115
Code Lines 60

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 57
CRAP Score 22.1366

Importance

Changes 0
Metric Value
dl 0
loc 115
ccs 57
cts 61
cp 0.9344
rs 2
c 0
b 0
f 0
cc 22
eloc 60
nc 7776
nop 1
crap 22.1366

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\ForeignKeyConstraint;
24
use Doctrine\DBAL\Schema\Identifier;
25
use Doctrine\DBAL\Schema\Index;
26
use Doctrine\DBAL\Schema\Sequence;
27
use Doctrine\DBAL\Schema\Table;
28
use Doctrine\DBAL\Schema\TableDiff;
29
use Doctrine\DBAL\TransactionIsolationLevel;
30
use Doctrine\DBAL\Types\BinaryType;
31
use function array_merge;
32
use function count;
33
use function explode;
34
use function func_get_arg;
35
use function func_num_args;
36
use function implode;
37
use function preg_match;
38
use function sprintf;
39
use function str_replace;
40
use function strlen;
41
use function strpos;
42
use function strtoupper;
43
use function substr;
44
45
/**
46
 * OraclePlatform.
47
 *
48
 * @since 2.0
49
 * @author Roman Borschel <[email protected]>
50
 * @author Lukas Smith <[email protected]> (PEAR MDB2 library)
51
 * @author Benjamin Eberlei <[email protected]>
52
 */
53
class OraclePlatform extends AbstractPlatform
54
{
55
    /**
56
     * Assertion for Oracle identifiers.
57
     *
58
     * @link http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements008.htm
59
     *
60
     * @param string $identifier
61
     *
62
     * @throws DBALException
63
     */
64 252
    public static function assertValidIdentifier($identifier)
65
    {
66 252
        if ( ! preg_match('(^(([a-zA-Z]{1}[a-zA-Z0-9_$#]{0,})|("[^"]+"))$)', $identifier)) {
67 90
            throw new DBALException("Invalid Oracle identifier");
68
        }
69 162
    }
70
71
    /**
72
     * {@inheritDoc}
73
     */
74
    public function getSubstringExpression($value, $position, $length = null)
75
    {
76
        if ($length !== null) {
77
            return "SUBSTR($value, $position, $length)";
78
        }
79
80
        return "SUBSTR($value, $position)";
81
    }
82
83
    /**
84
     * {@inheritDoc}
85
     */
86
    public function getNowExpression($type = 'timestamp')
87
    {
88
        switch ($type) {
89
            case 'date':
90
            case 'time':
91
            case 'timestamp':
92
            default:
93
                return 'TO_CHAR(CURRENT_TIMESTAMP, \'YYYY-MM-DD HH24:MI:SS\')';
94
        }
95
    }
96
97
    /**
98
     * {@inheritDoc}
99
     */
100
    public function getLocateExpression($str, $substr, $startPos = false)
101
    {
102
        if ($startPos == false) {
103
            return 'INSTR('.$str.', '.$substr.')';
104
        }
105
106
        return 'INSTR('.$str.', '.$substr.', '.$startPos.')';
107
    }
108
109
    /**
110
     * {@inheritDoc}
111
     */
112
    public function getGuidExpression()
113
    {
114
        return 'SYS_GUID()';
115
    }
116
117
    /**
118
     * {@inheritdoc}
119
     */
120
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
121
    {
122
        switch ($unit) {
123
            case DateIntervalUnit::MONTH:
124
            case DateIntervalUnit::QUARTER:
125
            case DateIntervalUnit::YEAR:
126
                switch ($unit) {
127
                    case DateIntervalUnit::QUARTER:
128
                        $interval *= 3;
129
                        break;
130
131
                    case DateIntervalUnit::YEAR:
132
                        $interval *= 12;
133
                        break;
134
                }
135
136
                return 'ADD_MONTHS(' . $date . ', ' . $operator . $interval . ')';
137
138
            default:
139
                $calculationClause = '';
140
141
                switch ($unit) {
142
                    case DateIntervalUnit::SECOND:
143
                        $calculationClause = '/24/60/60';
144
                        break;
145
146
                    case DateIntervalUnit::MINUTE:
147
                        $calculationClause = '/24/60';
148
                        break;
149
150
                    case DateIntervalUnit::HOUR:
151
                        $calculationClause = '/24';
152
                        break;
153
154
                    case DateIntervalUnit::WEEK:
155
                        $calculationClause = '*7';
156
                        break;
157
                }
158
159
                return '(' . $date . $operator . $interval . $calculationClause . ')';
160
        }
161
    }
162
163
    /**
164
     * {@inheritDoc}
165
     */
166
    public function getDateDiffExpression($date1, $date2)
167
    {
168
        return sprintf('TRUNC(%s) - TRUNC(%s)', $date1, $date2);
169
    }
170
171
    /**
172
     * {@inheritDoc}
173
     */
174 36
    public function getBitAndComparisonExpression($value1, $value2)
175
    {
176 36
        return 'BITAND('.$value1 . ', ' . $value2 . ')';
177
    }
178
179
    /**
180
     * {@inheritDoc}
181
     */
182 18
    public function getBitOrComparisonExpression($value1, $value2)
183
    {
184 18
        return '(' . $value1 . '-' .
185 18
                $this->getBitAndComparisonExpression($value1, $value2)
186 18
                . '+' . $value2 . ')';
187
    }
188
189
    /**
190
     * {@inheritDoc}
191
     *
192
     * Need to specifiy minvalue, since start with is hidden in the system and MINVALUE <= START WITH.
193
     * Therefore we can use MINVALUE to be able to get a hint what START WITH was for later introspection
194
     * in {@see listSequences()}
195
     */
196 108
    public function getCreateSequenceSQL(Sequence $sequence)
197
    {
198 108
        return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) .
199 108
               ' START WITH ' . $sequence->getInitialValue() .
200 108
               ' MINVALUE ' . $sequence->getInitialValue() .
201 108
               ' INCREMENT BY ' . $sequence->getAllocationSize() .
202 108
               $this->getSequenceCacheSQL($sequence);
203
    }
204
205
    /**
206
     * {@inheritDoc}
207
     */
208
    public function getAlterSequenceSQL(Sequence $sequence)
209
    {
210
        return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) .
211
               ' INCREMENT BY ' . $sequence->getAllocationSize()
212
               . $this->getSequenceCacheSQL($sequence);
213
    }
214
215
    /**
216
     * Cache definition for sequences
217
     *
218
     * @param Sequence $sequence
219
     *
220
     * @return string
221
     */
222 108
    private function getSequenceCacheSQL(Sequence $sequence)
223
    {
224 108
        if ($sequence->getCache() === 0) {
225 18
            return ' NOCACHE';
226 90
        } else if ($sequence->getCache() === 1) {
227 18
            return ' NOCACHE';
228 72
        } else if ($sequence->getCache() > 1) {
229 18
            return ' CACHE ' . $sequence->getCache();
230
        }
231
232 54
        return '';
233
    }
234
235
    /**
236
     * {@inheritDoc}
237
     */
238
    public function getSequenceNextValSQL($sequenceName)
239
    {
240
        return 'SELECT ' . $sequenceName . '.nextval FROM DUAL';
241
    }
242
243
    /**
244
     * {@inheritDoc}
245
     */
246 18
    public function getSetTransactionIsolationSQL($level)
247
    {
248 18
        return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level);
249
    }
250
251
    /**
252
     * {@inheritDoc}
253
     */
254 18
    protected function _getTransactionIsolationLevelSQL($level)
255
    {
256
        switch ($level) {
257 18
            case TransactionIsolationLevel::READ_UNCOMMITTED:
258 18
                return 'READ UNCOMMITTED';
259 18
            case TransactionIsolationLevel::READ_COMMITTED:
260 18
                return 'READ COMMITTED';
261 18
            case TransactionIsolationLevel::REPEATABLE_READ:
262 18
            case TransactionIsolationLevel::SERIALIZABLE:
263 18
                return 'SERIALIZABLE';
264
            default:
265
                return parent::_getTransactionIsolationLevelSQL($level);
266
        }
267
    }
268
269
    /**
270
     * {@inheritDoc}
271
     */
272 18
    public function getBooleanTypeDeclarationSQL(array $field)
273
    {
274 18
        return 'NUMBER(1)';
275
    }
276
277
    /**
278
     * {@inheritDoc}
279
     */
280 180
    public function getIntegerTypeDeclarationSQL(array $field)
281
    {
282 180
        return 'NUMBER(10)';
283
    }
284
285
    /**
286
     * {@inheritDoc}
287
     */
288
    public function getBigIntTypeDeclarationSQL(array $field)
289
    {
290
        return 'NUMBER(20)';
291
    }
292
293
    /**
294
     * {@inheritDoc}
295
     */
296
    public function getSmallIntTypeDeclarationSQL(array $field)
297
    {
298
        return 'NUMBER(5)';
299
    }
300
301
    /**
302
     * {@inheritDoc}
303
     */
304
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
305
    {
306
        return 'TIMESTAMP(0)';
307
    }
308
309
    /**
310
     * {@inheritDoc}
311
     */
312
    public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration)
313
    {
314
        return 'TIMESTAMP(0) WITH TIME ZONE';
315
    }
316
317
    /**
318
     * {@inheritDoc}
319
     */
320
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
321
    {
322
        return 'DATE';
323
    }
324
325
    /**
326
     * {@inheritDoc}
327
     */
328
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
329
    {
330
        return 'DATE';
331
    }
332
333
    /**
334
     * {@inheritDoc}
335
     */
336
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
337
    {
338
        return '';
339
    }
340
341
    /**
342
     * {@inheritDoc}
343
     */
344 252
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
345
    {
346 252
        return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(2000)')
347 252
                : ($length ? 'VARCHAR2(' . $length . ')' : 'VARCHAR2(4000)');
348
    }
349
350
    /**
351
     * {@inheritdoc}
352
     */
353 18
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
354
    {
355 18
        return 'RAW(' . ($length ?: $this->getBinaryMaxLength()) . ')';
356
    }
357
358
    /**
359
     * {@inheritdoc}
360
     */
361 36
    public function getBinaryMaxLength()
362
    {
363 36
        return 2000;
364
    }
365
366
    /**
367
     * {@inheritDoc}
368
     */
369 36
    public function getClobTypeDeclarationSQL(array $field)
370
    {
371 36
        return 'CLOB';
372
    }
373
374
    /**
375
     * {@inheritDoc}
376
     */
377
    public function getListDatabasesSQL()
378
    {
379
        return 'SELECT username FROM all_users';
380
    }
381
382
    /**
383
     * {@inheritDoc}
384
     */
385 18
    public function getListSequencesSQL($database)
386
    {
387 18
        $database = $this->normalizeIdentifier($database);
388 18
        $database = $this->quoteStringLiteral($database->getName());
389
390
        return "SELECT sequence_name, min_value, increment_by FROM sys.all_sequences ".
391 18
               "WHERE SEQUENCE_OWNER = " . $database;
392
    }
393
394
    /**
395
     * {@inheritDoc}
396
     */
397 198
    protected function _getCreateTableSQL($table, array $columns, array $options = [])
398
    {
399 198
        $indexes            = $options['indexes'] ?? [];
400 198
        $options['indexes'] = [];
401 198
        $sql                = parent::_getCreateTableSQL($table, $columns, $options);
402
403 198
        foreach ($columns as $name => $column) {
404 198
            if (isset($column['sequence'])) {
405
                $sql[] = $this->getCreateSequenceSQL($column['sequence'], 1);
0 ignored issues
show
Unused Code introduced by
The call to Doctrine\DBAL\Platforms\...:getCreateSequenceSQL() has too many arguments starting with 1. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

405
                /** @scrutinizer ignore-call */ 
406
                $sql[] = $this->getCreateSequenceSQL($column['sequence'], 1);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
406
            }
407
408 198
            if (isset($column['autoincrement']) && $column['autoincrement'] ||
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: {currentAssign}, Probably Intended Meaning: {alternativeAssign}
Loading history...
409 198
               (isset($column['autoinc']) && $column['autoinc'])) {
410 198
                $sql = array_merge($sql, $this->getCreateAutoincrementSql($name, $table));
411
            }
412
        }
413
414 198
        if (isset($indexes) && ! empty($indexes)) {
415 54
            foreach ($indexes as $index) {
416 54
                $sql[] = $this->getCreateIndexSQL($index, $table);
417
            }
418
        }
419
420 198
        return $sql;
421
    }
422
423
    /**
424
     * {@inheritDoc}
425
     *
426
     * @license New BSD License
427
     * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaOracleReader.html
428
     */
429 18
    public function getListTableIndexesSQL($table, $currentDatabase = null)
430
    {
431 18
        $table = $this->normalizeIdentifier($table);
432 18
        $table = $this->quoteStringLiteral($table->getName());
433
434
        return "SELECT uind_col.index_name AS name,
435
                       (
436
                           SELECT uind.index_type
437
                           FROM   user_indexes uind
438
                           WHERE  uind.index_name = uind_col.index_name
439
                       ) AS type,
440
                       decode(
441
                           (
442
                               SELECT uind.uniqueness
443
                               FROM   user_indexes uind
444
                               WHERE  uind.index_name = uind_col.index_name
445
                           ),
446
                           'NONUNIQUE',
447
                           0,
448
                           'UNIQUE',
449
                           1
450
                       ) AS is_unique,
451
                       uind_col.column_name AS column_name,
452
                       uind_col.column_position AS column_pos,
453
                       (
454
                           SELECT ucon.constraint_type
455
                           FROM   user_constraints ucon
456
                           WHERE  ucon.index_name = uind_col.index_name
457
                       ) AS is_primary
458
             FROM      user_ind_columns uind_col
459 18
             WHERE     uind_col.table_name = " . $table . "
460
             ORDER BY  uind_col.column_position ASC";
461
    }
462
463
    /**
464
     * {@inheritDoc}
465
     */
466
    public function getListTablesSQL()
467
    {
468
        return 'SELECT * FROM sys.user_tables';
469
    }
470
471
    /**
472
     * {@inheritDoc}
473
     */
474
    public function getListViewsSQL($database)
475
    {
476
        return 'SELECT view_name, text FROM sys.user_views';
477
    }
478
479
    /**
480
     * {@inheritDoc}
481
     */
482
    public function getCreateViewSQL($name, $sql)
483
    {
484
        return 'CREATE VIEW ' . $name . ' AS ' . $sql;
485
    }
486
487
    /**
488
     * {@inheritDoc}
489
     */
490
    public function getDropViewSQL($name)
491
    {
492
        return 'DROP VIEW '. $name;
493
    }
494
495
    /**
496
     * @param string $name
497
     * @param string $table
498
     * @param int    $start
499
     *
500
     * @return array
501
     */
502 54
    public function getCreateAutoincrementSql($name, $table, $start = 1)
503
    {
504 54
        $tableIdentifier = $this->normalizeIdentifier($table);
505 54
        $quotedTableName = $tableIdentifier->getQuotedName($this);
506 54
        $unquotedTableName = $tableIdentifier->getName();
507
508 54
        $nameIdentifier = $this->normalizeIdentifier($name);
509 54
        $quotedName = $nameIdentifier->getQuotedName($this);
510 54
        $unquotedName = $nameIdentifier->getName();
511
512 54
        $sql = [];
513
514 54
        $autoincrementIdentifierName = $this->getAutoincrementIdentifierName($tableIdentifier);
515
516 54
        $idx = new Index($autoincrementIdentifierName, [$quotedName], true, true);
517
518 54
        $sql[] = 'DECLARE
519
  constraints_Count NUMBER;
520
BEGIN
521 54
  SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count FROM USER_CONSTRAINTS WHERE TABLE_NAME = \'' . $unquotedTableName . '\' AND CONSTRAINT_TYPE = \'P\';
522
  IF constraints_Count = 0 OR constraints_Count = \'\' THEN
523 54
    EXECUTE IMMEDIATE \''.$this->getCreateConstraintSQL($idx, $quotedTableName).'\';
524
  END IF;
525
END;';
526
527 54
        $sequenceName = $this->getIdentitySequenceName(
528 54
            $tableIdentifier->isQuoted() ? $quotedTableName : $unquotedTableName,
529 54
            $nameIdentifier->isQuoted() ? $quotedName : $unquotedName
530
        );
531 54
        $sequence = new Sequence($sequenceName, $start);
532 54
        $sql[] = $this->getCreateSequenceSQL($sequence);
533
534 54
        $sql[] = 'CREATE TRIGGER ' . $autoincrementIdentifierName . '
535
   BEFORE INSERT
536 54
   ON ' . $quotedTableName . '
537
   FOR EACH ROW
538
DECLARE
539
   last_Sequence NUMBER;
540
   last_InsertID NUMBER;
541
BEGIN
542 54
   SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL;
543 54
   IF (:NEW.' . $quotedName . ' IS NULL OR :NEW.'.$quotedName.' = 0) THEN
544 54
      SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL;
545
   ELSE
546
      SELECT NVL(Last_Number, 0) INTO last_Sequence
547
        FROM User_Sequences
548 54
       WHERE Sequence_Name = \'' . $sequence->getName() . '\';
549 54
      SELECT :NEW.' . $quotedName . ' INTO last_InsertID FROM DUAL;
550
      WHILE (last_InsertID > last_Sequence) LOOP
551 54
         SELECT ' . $sequenceName . '.NEXTVAL INTO last_Sequence FROM DUAL;
552
      END LOOP;
553
   END IF;
554
END;';
555
556 54
        return $sql;
557
    }
558
559
    /**
560
     * Returns the SQL statements to drop the autoincrement for the given table name.
561
     *
562
     * @param string $table The table name to drop the autoincrement for.
563
     *
564
     * @return array
565
     */
566 54
    public function getDropAutoincrementSql($table)
567
    {
568 54
        $table = $this->normalizeIdentifier($table);
569 54
        $autoincrementIdentifierName = $this->getAutoincrementIdentifierName($table);
570 54
        $identitySequenceName = $this->getIdentitySequenceName(
571 54
            $table->isQuoted() ? $table->getQuotedName($this) : $table->getName(),
572 54
            ''
573
        );
574
575
        return [
576 54
            'DROP TRIGGER ' . $autoincrementIdentifierName,
577 54
            $this->getDropSequenceSQL($identitySequenceName),
578 54
            $this->getDropConstraintSQL($autoincrementIdentifierName, $table->getQuotedName($this)),
579
        ];
580
    }
581
582
    /**
583
     * Normalizes the given identifier.
584
     *
585
     * Uppercases the given identifier if it is not quoted by intention
586
     * to reflect Oracle's internal auto uppercasing strategy of unquoted identifiers.
587
     *
588
     * @param string $name The identifier to normalize.
589
     *
590
     * @return Identifier The normalized identifier.
591
     */
592 288
    private function normalizeIdentifier($name)
593
    {
594 288
        $identifier = new Identifier($name);
595
596 288
        return $identifier->isQuoted() ? $identifier : new Identifier(strtoupper($name));
597
    }
598
599
    /**
600
     * Returns the autoincrement primary key identifier name for the given table identifier.
601
     *
602
     * Quotes the autoincrement primary key identifier name
603
     * if the given table name is quoted by intention.
604
     *
605
     * @param Identifier $table The table identifier to return the autoincrement primary key identifier name for.
606
     *
607
     * @return string
608
     */
609 108
    private function getAutoincrementIdentifierName(Identifier $table)
610
    {
611 108
        $identifierName = $table->getName() . '_AI_PK';
612
613 108
        return $table->isQuoted()
614 36
            ? $this->quoteSingleIdentifier($identifierName)
615 108
            : $identifierName;
616
    }
617
618
    /**
619
     * {@inheritDoc}
620
     */
621 18
    public function getListTableForeignKeysSQL($table)
622
    {
623 18
        $table = $this->normalizeIdentifier($table);
624 18
        $table = $this->quoteStringLiteral($table->getName());
625
626
        return "SELECT alc.constraint_name,
627
          alc.DELETE_RULE,
628
          cols.column_name \"local_column\",
629
          cols.position,
630
          (
631
              SELECT r_cols.table_name
632
              FROM   user_cons_columns r_cols
633
              WHERE  alc.r_constraint_name = r_cols.constraint_name
634
              AND    r_cols.position = cols.position
635
          ) AS \"references_table\",
636
          (
637
              SELECT r_cols.column_name
638
              FROM   user_cons_columns r_cols
639
              WHERE  alc.r_constraint_name = r_cols.constraint_name
640
              AND    r_cols.position = cols.position
641
          ) AS \"foreign_column\"
642
     FROM user_cons_columns cols
643
     JOIN user_constraints alc
644
       ON alc.constraint_name = cols.constraint_name
645
      AND alc.constraint_type = 'R'
646 18
      AND alc.table_name = " . $table . "
647
    ORDER BY cols.constraint_name ASC, cols.position ASC";
648
    }
649
650
    /**
651
     * {@inheritDoc}
652
     */
653 18
    public function getListTableConstraintsSQL($table)
654
    {
655 18
        $table = $this->normalizeIdentifier($table);
656 18
        $table = $this->quoteStringLiteral($table->getName());
657
658 18
        return "SELECT * FROM user_constraints WHERE table_name = " . $table;
659
    }
660
661
    /**
662
     * {@inheritDoc}
663
     */
664 90
    public function getListTableColumnsSQL($table, $database = null)
665
    {
666 90
        $table = $this->normalizeIdentifier($table);
667 90
        $table = $this->quoteStringLiteral($table->getName());
668
669 90
        $tabColumnsTableName = "user_tab_columns";
670 90
        $colCommentsTableName = "user_col_comments";
671 90
        $tabColumnsOwnerCondition = '';
672 90
        $colCommentsOwnerCondition = '';
673
674 90
        if (null !== $database && '/' !== $database) {
675 36
            $database = $this->normalizeIdentifier($database);
676 36
            $database = $this->quoteStringLiteral($database->getName());
677 36
            $tabColumnsTableName = "all_tab_columns";
678 36
            $colCommentsTableName = "all_col_comments";
679 36
            $tabColumnsOwnerCondition = "AND c.owner = " . $database;
680 36
            $colCommentsOwnerCondition = "AND d.OWNER = c.OWNER";
681
        }
682
683
        return "SELECT   c.*,
684
                         (
685
                             SELECT d.comments
686 90
                             FROM   $colCommentsTableName d
687 90
                             WHERE  d.TABLE_NAME = c.TABLE_NAME " . $colCommentsOwnerCondition . "
688
                             AND    d.COLUMN_NAME = c.COLUMN_NAME
689
                         ) AS comments
690 90
                FROM     $tabColumnsTableName c
691 90
                WHERE    c.table_name = " . $table . " $tabColumnsOwnerCondition
692
                ORDER BY c.column_id";
693
    }
694
695
    /**
696
     * {@inheritDoc}
697
     */
698 54
    public function getDropSequenceSQL($sequence)
699
    {
700 54
        if ($sequence instanceof Sequence) {
701
            $sequence = $sequence->getQuotedName($this);
702
        }
703
704 54
        return 'DROP SEQUENCE ' . $sequence;
705
    }
706
707
    /**
708
     * {@inheritDoc}
709
     */
710 36
    public function getDropForeignKeySQL($foreignKey, $table)
711
    {
712 36
        if (! $foreignKey instanceof ForeignKeyConstraint) {
713 18
            $foreignKey = new Identifier($foreignKey);
714
        }
715
716 36
        if (! $table instanceof Table) {
717 36
            $table = new Identifier($table);
718
        }
719
720 36
        $foreignKey = $foreignKey->getQuotedName($this);
721 36
        $table = $table->getQuotedName($this);
722
723 36
        return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey;
724
    }
725
726
    /**
727
     * {@inheritdoc}
728
     */
729 180
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
730
    {
731 180
        $referentialAction = null;
732
733 180
        if ($foreignKey->hasOption('onDelete')) {
734 72
            $referentialAction = $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
735
        }
736
737 180
        return $referentialAction ? ' ON DELETE ' . $referentialAction : '';
738
    }
739
740
    /**
741
     * {@inheritdoc}
742
     */
743 180
    public function getForeignKeyReferentialActionSQL($action)
744
    {
745 180
        $action = strtoupper($action);
746
747
        switch ($action) {
748 180
            case 'RESTRICT': // RESTRICT is not supported, therefore falling back to NO ACTION.
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
749 144
            case 'NO ACTION':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
750
                // NO ACTION cannot be declared explicitly,
751
                // therefore returning empty string to indicate to OMIT the referential clause.
752 72
                return '';
753
754 108
            case 'CASCADE':
755 54
            case 'SET NULL':
756 90
                return $action;
757
758
            default:
759
                // SET DEFAULT is not supported, throw exception instead.
760 18
                throw new \InvalidArgumentException('Invalid foreign key action: ' . $action);
761
        }
762
    }
763
764
    /**
765
     * {@inheritDoc}
766
     */
767 18
    public function getDropDatabaseSQL($database)
768
    {
769 18
        return 'DROP USER ' . $database . ' CASCADE';
770
    }
771
772
    /**
773
     * {@inheritDoc}
774
     */
775 288
    public function getAlterTableSQL(TableDiff $diff)
776
    {
777 288
        $sql = [];
778 288
        $commentsSQL = [];
779 288
        $columnSql = [];
780
781 288
        $fields = [];
782
783 288
        foreach ($diff->addedColumns as $column) {
784 72
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
785
                continue;
786
            }
787
788 72
            $fields[] = $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
789 72
            if ($comment = $this->getColumnComment($column)) {
790 18
                $commentsSQL[] = $this->getCommentOnColumnSQL(
791 18
                    $diff->getName($this)->getQuotedName($this),
792 18
                    $column->getQuotedName($this),
793 72
                    $comment
794
                );
795
            }
796
        }
797
798 288
        if (count($fields)) {
799 72
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ADD (' . implode(', ', $fields) . ')';
800
        }
801
802 288
        $fields = [];
803 288
        foreach ($diff->changedColumns as $columnDiff) {
804 162
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
805
                continue;
806
            }
807
808
            /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */
809 162
            $column = $columnDiff->column;
810
811
            // Do not generate column alteration clause if type is binary and only fixed property has changed.
812
            // Oracle only supports binary type columns with variable length.
813
            // Avoids unnecessary table alteration statements.
814 162
            if ($column->getType() instanceof BinaryType &&
815 162
                $columnDiff->hasChanged('fixed') &&
816 162
                count($columnDiff->changedProperties) === 1
817
            ) {
818 18
                continue;
819
            }
820
821 144
            $columnHasChangedComment = $columnDiff->hasChanged('comment');
822
823
            /**
824
             * Do not add query part if only comment has changed
825
             */
826 144
            if ( ! ($columnHasChangedComment && count($columnDiff->changedProperties) === 1)) {
827 108
                $columnInfo = $column->toArray();
828
829 108
                if ( ! $columnDiff->hasChanged('notnull')) {
830 72
                    unset($columnInfo['notnull']);
831
                }
832
833 108
                $fields[] = $column->getQuotedName($this) . $this->getColumnDeclarationSQL('', $columnInfo);
834
            }
835
836 144
            if ($columnHasChangedComment) {
837 36
                $commentsSQL[] = $this->getCommentOnColumnSQL(
838 36
                    $diff->getName($this)->getQuotedName($this),
839 36
                    $column->getQuotedName($this),
840 144
                    $this->getColumnComment($column)
841
                );
842
            }
843
        }
844
845 288
        if (count($fields)) {
846 108
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' MODIFY (' . implode(', ', $fields) . ')';
847
        }
848
849 288
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
850 72
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
851
                continue;
852
            }
853
854 72
            $oldColumnName = new Identifier($oldColumnName);
855
856 72
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) .
857 72
                ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) .' TO ' . $column->getQuotedName($this);
858
        }
859
860 288
        $fields = [];
861 288
        foreach ($diff->removedColumns as $column) {
862 54
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
863
                continue;
864
            }
865
866 54
            $fields[] = $column->getQuotedName($this);
867
        }
868
869 288
        if (count($fields)) {
870 54
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' DROP (' . implode(', ', $fields).')';
871
        }
872
873 288
        $tableSql = [];
874
875 288
        if ( ! $this->onSchemaAlterTable($diff, $tableSql)) {
876 288
            $sql = array_merge($sql, $commentsSQL);
877
878 288
            if ($diff->newName !== false) {
879 36
                $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' RENAME TO ' . $diff->getNewName()->getQuotedName($this);
880
            }
881
882 288
            $sql = array_merge(
883 288
                $this->getPreAlterTableIndexForeignKeySQL($diff),
884 288
                $sql,
885 288
                $this->getPostAlterTableIndexForeignKeySQL($diff)
886
            );
887
        }
888
889 288
        return array_merge($sql, $tableSql, $columnSql);
890
    }
891
892
    /**
893
     * {@inheritdoc}
894
     */
895 342
    public function getColumnDeclarationSQL($name, array $field)
896
    {
897 342
        if (isset($field['columnDefinition'])) {
898 18
            $columnDef = $this->getCustomTypeDeclarationSQL($field);
899
        } else {
900 324
            $default = $this->getDefaultValueDeclarationSQL($field);
901
902 324
            $notnull = '';
903
904 324
            if (isset($field['notnull'])) {
905 288
                $notnull = $field['notnull'] ? ' NOT NULL' : ' NULL';
906
            }
907
908 324
            $unique = (isset($field['unique']) && $field['unique']) ?
909 324
                ' ' . $this->getUniqueFieldDeclarationSQL() : '';
910
911 324
            $check = (isset($field['check']) && $field['check']) ?
912 324
                ' ' . $field['check'] : '';
913
914 324
            $typeDecl = $field['type']->getSQLDeclaration($field, $this);
915 324
            $columnDef = $typeDecl . $default . $notnull . $unique . $check;
916
        }
917
918 342
        return $name . ' ' . $columnDef;
919
    }
920
921
    /**
922
     * {@inheritdoc}
923
     */
924 90
    protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
925
    {
926 90
        if (strpos($tableName, '.') !== false) {
927 36
            list($schema) = explode('.', $tableName);
928 36
            $oldIndexName = $schema . '.' . $oldIndexName;
929
        }
930
931 90
        return ['ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)];
932
    }
933
934
    /**
935
     * {@inheritDoc}
936
     */
937
    public function prefersSequences()
938
    {
939
        return true;
940
    }
941
942
    /**
943
     * {@inheritdoc}
944
     */
945 18
    public function usesSequenceEmulatedIdentityColumns()
946
    {
947 18
        return true;
948
    }
949
950
    /**
951
     * {@inheritdoc}
952
     */
953 126
    public function getIdentitySequenceName($tableName, $columnName)
954
    {
955 126
        $table = new Identifier($tableName);
956
957
        // No usage of column name to preserve BC compatibility with <2.5
958 126
        $identitySequenceName = $table->getName() . '_SEQ';
959
960 126
        if ($table->isQuoted()) {
961 54
            $identitySequenceName = '"' . $identitySequenceName . '"';
962
        }
963
964 126
        $identitySequenceIdentifier = $this->normalizeIdentifier($identitySequenceName);
965
966 126
        return $identitySequenceIdentifier->getQuotedName($this);
967
    }
968
969
    /**
970
     * {@inheritDoc}
971
     */
972 216
    public function supportsCommentOnStatement()
973
    {
974 216
        return true;
975
    }
976
977
    /**
978
     * {@inheritDoc}
979
     */
980 36
    public function getName()
981
    {
982 36
        return 'oracle';
983
    }
984
985
    /**
986
     * {@inheritDoc}
987
     */
988 108
    protected function doModifyLimitQuery($query, $limit, $offset = null)
989
    {
990 108
        if ($limit === null && $offset === null) {
991
            return $query;
992
        }
993
994 108
        if (preg_match('/^\s*SELECT/i', $query)) {
995 108
            if (!preg_match('/\sFROM\s/i', $query)) {
996
                $query .= " FROM dual";
997
            }
998
999 108
            $columns = ['a.*'];
1000
1001 108
            if ($offset > 0) {
1002 36
                $columns[] = 'ROWNUM AS doctrine_rownum';
1003
            }
1004
1005 108
            $query = sprintf('SELECT %s FROM (%s) a', implode(', ', $columns), $query);
1006
1007 108
            if ($limit !== null) {
1008 90
                $query .= sprintf(' WHERE ROWNUM <= %d', $offset + $limit);
1009
            }
1010
1011 108
            if ($offset > 0) {
1012 36
                $query = sprintf('SELECT * FROM (%s) WHERE doctrine_rownum >= %d', $query, $offset + 1);
1013
            }
1014
        }
1015
1016 108
        return $query;
1017
    }
1018
1019
    /**
1020
     * {@inheritDoc}
1021
     *
1022
     * Oracle returns all column names in SQL result sets in uppercase.
1023
     */
1024
    public function getSQLResultCasing($column)
1025
    {
1026
        return strtoupper($column);
1027
    }
1028
1029
    /**
1030
     * {@inheritDoc}
1031
     */
1032
    public function getCreateTemporaryTableSnippetSQL()
1033
    {
1034
        return "CREATE GLOBAL TEMPORARY TABLE";
1035
    }
1036
1037
    /**
1038
     * {@inheritDoc}
1039
     */
1040
    public function getDateTimeTzFormatString()
1041
    {
1042
        return 'Y-m-d H:i:sP';
1043
    }
1044
1045
    /**
1046
     * {@inheritDoc}
1047
     */
1048
    public function getDateFormatString()
1049
    {
1050
        return 'Y-m-d 00:00:00';
1051
    }
1052
1053
    /**
1054
     * {@inheritDoc}
1055
     */
1056
    public function getTimeFormatString()
1057
    {
1058
        return '1900-01-01 H:i:s';
1059
    }
1060
1061
    /**
1062
     * {@inheritDoc}
1063
     */
1064
    public function fixSchemaElementName($schemaElementName)
1065
    {
1066
        if (strlen($schemaElementName) > 30) {
1067
            // Trim it
1068
            return substr($schemaElementName, 0, 30);
1069
        }
1070
1071
        return $schemaElementName;
1072
    }
1073
1074
    /**
1075
     * {@inheritDoc}
1076
     */
1077
    public function getMaxIdentifierLength()
1078
    {
1079
        return 30;
1080
    }
1081
1082
    /**
1083
     * {@inheritDoc}
1084
     */
1085
    public function supportsSequences()
1086
    {
1087
        return true;
1088
    }
1089
1090
    /**
1091
     * {@inheritDoc}
1092
     */
1093
    public function supportsForeignKeyOnUpdate()
1094
    {
1095
        return false;
1096
    }
1097
1098
    /**
1099
     * {@inheritDoc}
1100
     */
1101
    public function supportsReleaseSavepoints()
1102
    {
1103
        return false;
1104
    }
1105
1106
    /**
1107
     * {@inheritDoc}
1108
     */
1109 18
    public function getTruncateTableSQL($tableName, $cascade = false)
1110
    {
1111 18
        $tableIdentifier = new Identifier($tableName);
1112
1113 18
        return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this);
1114
    }
1115
1116
    /**
1117
     * {@inheritDoc}
1118
     */
1119
    public function getDummySelectSQL()
1120
    {
1121
        $expression = func_num_args() > 0 ? func_get_arg(0) : '1';
1122
1123
        return sprintf('SELECT %s FROM DUAL', $expression);
1124
    }
1125
1126
    /**
1127
     * {@inheritDoc}
1128
     */
1129 90
    protected function initializeDoctrineTypeMappings()
1130
    {
1131 90
        $this->doctrineTypeMapping = [
1132
            'integer'           => 'integer',
1133
            'number'            => 'integer',
1134
            'pls_integer'       => 'boolean',
1135
            'binary_integer'    => 'boolean',
1136
            'varchar'           => 'string',
1137
            'varchar2'          => 'string',
1138
            'nvarchar2'         => 'string',
1139
            'char'              => 'string',
1140
            'nchar'             => 'string',
1141
            'date'              => 'date',
1142
            'timestamp'         => 'datetime',
1143
            'timestamptz'       => 'datetimetz',
1144
            'float'             => 'float',
1145
            'binary_float'      => 'float',
1146
            'binary_double'     => 'float',
1147
            'long'              => 'string',
1148
            'clob'              => 'text',
1149
            'nclob'             => 'text',
1150
            'raw'               => 'binary',
1151
            'long raw'          => 'blob',
1152
            'rowid'             => 'string',
1153
            'urowid'            => 'string',
1154
            'blob'              => 'blob',
1155
        ];
1156 90
    }
1157
1158
    /**
1159
     * {@inheritDoc}
1160
     */
1161
    public function releaseSavePoint($savepoint)
1162
    {
1163
        return '';
1164
    }
1165
1166
    /**
1167
     * {@inheritDoc}
1168
     */
1169 882
    protected function getReservedKeywordsClass()
1170
    {
1171 882
        return Keywords\OracleKeywords::class;
1172
    }
1173
1174
    /**
1175
     * {@inheritDoc}
1176
     */
1177 18
    public function getBlobTypeDeclarationSQL(array $field)
1178
    {
1179 18
        return 'BLOB';
1180
    }
1181
1182
    /**
1183
     * {@inheritdoc}
1184
     */
1185 306
    public function quoteStringLiteral($str)
1186
    {
1187 306
        $str = str_replace('\\', '\\\\', $str); // Oracle requires backslashes to be escaped aswell.
1188
1189 306
        return parent::quoteStringLiteral($str);
1190
    }
1191
}
1192