Passed
Pull Request — master (#3133)
by Michael
17:35
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 266
    public static function assertValidIdentifier($identifier)
65
    {
66 266
        if ( ! preg_match('(^(([a-zA-Z]{1}[a-zA-Z0-9_$#]{0,})|("[^"]+"))$)', $identifier)) {
67 95
            throw new DBALException("Invalid Oracle identifier");
68
        }
69 171
    }
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 38
    public function getBitAndComparisonExpression($value1, $value2)
175
    {
176 38
        return 'BITAND('.$value1 . ', ' . $value2 . ')';
177
    }
178
179
    /**
180
     * {@inheritDoc}
181
     */
182 19
    public function getBitOrComparisonExpression($value1, $value2)
183
    {
184 19
        return '(' . $value1 . '-' .
185 19
                $this->getBitAndComparisonExpression($value1, $value2)
186 19
                . '+' . $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 114
    public function getCreateSequenceSQL(Sequence $sequence)
197
    {
198 114
        return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) .
199 114
               ' START WITH ' . $sequence->getInitialValue() .
200 114
               ' MINVALUE ' . $sequence->getInitialValue() .
201 114
               ' INCREMENT BY ' . $sequence->getAllocationSize() .
202 114
               $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 114
    private function getSequenceCacheSQL(Sequence $sequence)
223
    {
224 114
        if ($sequence->getCache() === 0) {
225 19
            return ' NOCACHE';
226 95
        } else if ($sequence->getCache() === 1) {
227 19
            return ' NOCACHE';
228 76
        } else if ($sequence->getCache() > 1) {
229 19
            return ' CACHE ' . $sequence->getCache();
230
        }
231
232 57
        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 19
    public function getSetTransactionIsolationSQL($level)
247
    {
248 19
        return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level);
249
    }
250
251
    /**
252
     * {@inheritDoc}
253
     */
254 19
    protected function _getTransactionIsolationLevelSQL($level)
255
    {
256
        switch ($level) {
257 19
            case TransactionIsolationLevel::READ_UNCOMMITTED:
258 19
                return 'READ UNCOMMITTED';
259 19
            case TransactionIsolationLevel::READ_COMMITTED:
260 19
                return 'READ COMMITTED';
261 19
            case TransactionIsolationLevel::REPEATABLE_READ:
262 19
            case TransactionIsolationLevel::SERIALIZABLE:
263 19
                return 'SERIALIZABLE';
264
            default:
265
                return parent::_getTransactionIsolationLevelSQL($level);
266
        }
267
    }
268
269
    /**
270
     * {@inheritDoc}
271
     */
272 19
    public function getBooleanTypeDeclarationSQL(array $field)
273
    {
274 19
        return 'NUMBER(1)';
275
    }
276
277
    /**
278
     * {@inheritDoc}
279
     */
280 190
    public function getIntegerTypeDeclarationSQL(array $field)
281
    {
282 190
        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 266
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
345
    {
346 266
        return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(2000)')
347 266
                : ($length ? 'VARCHAR2(' . $length . ')' : 'VARCHAR2(4000)');
348
    }
349
350
    /**
351
     * {@inheritdoc}
352
     */
353 19
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
354
    {
355 19
        return 'RAW(' . ($length ?: $this->getBinaryMaxLength()) . ')';
356
    }
357
358
    /**
359
     * {@inheritdoc}
360
     */
361 38
    public function getBinaryMaxLength()
362
    {
363 38
        return 2000;
364
    }
365
366
    /**
367
     * {@inheritDoc}
368
     */
369 38
    public function getClobTypeDeclarationSQL(array $field)
370
    {
371 38
        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 19
    public function getListSequencesSQL($database)
386
    {
387 19
        $database = $this->normalizeIdentifier($database);
388 19
        $database = $this->quoteStringLiteral($database->getName());
389
390
        return "SELECT sequence_name, min_value, increment_by FROM sys.all_sequences ".
391 19
               "WHERE SEQUENCE_OWNER = " . $database;
392
    }
393
394
    /**
395
     * {@inheritDoc}
396
     */
397 209
    protected function _getCreateTableSQL($table, array $columns, array $options = [])
398
    {
399 209
        $indexes            = $options['indexes'] ?? [];
400 209
        $options['indexes'] = [];
401 209
        $sql                = parent::_getCreateTableSQL($table, $columns, $options);
402
403 209
        foreach ($columns as $name => $column) {
404 209
            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 209
            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 209
               (isset($column['autoinc']) && $column['autoinc'])) {
410 209
                $sql = array_merge($sql, $this->getCreateAutoincrementSql($name, $table));
411
            }
412
        }
413
414 209
        if (isset($indexes) && ! empty($indexes)) {
415 57
            foreach ($indexes as $index) {
416 57
                $sql[] = $this->getCreateIndexSQL($index, $table);
417
            }
418
        }
419
420 209
        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 19
    public function getListTableIndexesSQL($table, $currentDatabase = null)
430
    {
431 19
        $table = $this->normalizeIdentifier($table);
432 19
        $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 19
             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 57
    public function getCreateAutoincrementSql($name, $table, $start = 1)
503
    {
504 57
        $tableIdentifier = $this->normalizeIdentifier($table);
505 57
        $quotedTableName = $tableIdentifier->getQuotedName($this);
506 57
        $unquotedTableName = $tableIdentifier->getName();
507
508 57
        $nameIdentifier = $this->normalizeIdentifier($name);
509 57
        $quotedName = $nameIdentifier->getQuotedName($this);
510 57
        $unquotedName = $nameIdentifier->getName();
511
512 57
        $sql = [];
513
514 57
        $autoincrementIdentifierName = $this->getAutoincrementIdentifierName($tableIdentifier);
515
516 57
        $idx = new Index($autoincrementIdentifierName, [$quotedName], true, true);
517
518 57
        $sql[] = 'DECLARE
519
  constraints_Count NUMBER;
520
BEGIN
521 57
  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 57
    EXECUTE IMMEDIATE \''.$this->getCreateConstraintSQL($idx, $quotedTableName).'\';
524
  END IF;
525
END;';
526
527 57
        $sequenceName = $this->getIdentitySequenceName(
528 57
            $tableIdentifier->isQuoted() ? $quotedTableName : $unquotedTableName,
529 57
            $nameIdentifier->isQuoted() ? $quotedName : $unquotedName
530
        );
531 57
        $sequence = new Sequence($sequenceName, $start);
532 57
        $sql[] = $this->getCreateSequenceSQL($sequence);
533
534 57
        $sql[] = 'CREATE TRIGGER ' . $autoincrementIdentifierName . '
535
   BEFORE INSERT
536 57
   ON ' . $quotedTableName . '
537
   FOR EACH ROW
538
DECLARE
539
   last_Sequence NUMBER;
540
   last_InsertID NUMBER;
541
BEGIN
542 57
   SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL;
543 57
   IF (:NEW.' . $quotedName . ' IS NULL OR :NEW.'.$quotedName.' = 0) THEN
544 57
      SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL;
545
   ELSE
546
      SELECT NVL(Last_Number, 0) INTO last_Sequence
547
        FROM User_Sequences
548 57
       WHERE Sequence_Name = \'' . $sequence->getName() . '\';
549 57
      SELECT :NEW.' . $quotedName . ' INTO last_InsertID FROM DUAL;
550
      WHILE (last_InsertID > last_Sequence) LOOP
551 57
         SELECT ' . $sequenceName . '.NEXTVAL INTO last_Sequence FROM DUAL;
552
      END LOOP;
553
   END IF;
554
END;';
555
556 57
        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 57
    public function getDropAutoincrementSql($table)
567
    {
568 57
        $table = $this->normalizeIdentifier($table);
569 57
        $autoincrementIdentifierName = $this->getAutoincrementIdentifierName($table);
570 57
        $identitySequenceName = $this->getIdentitySequenceName(
571 57
            $table->isQuoted() ? $table->getQuotedName($this) : $table->getName(),
572 57
            ''
573
        );
574
575
        return [
576 57
            'DROP TRIGGER ' . $autoincrementIdentifierName,
577 57
            $this->getDropSequenceSQL($identitySequenceName),
578 57
            $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 304
    private function normalizeIdentifier($name)
593
    {
594 304
        $identifier = new Identifier($name);
595
596 304
        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 114
    private function getAutoincrementIdentifierName(Identifier $table)
610
    {
611 114
        $identifierName = $table->getName() . '_AI_PK';
612
613 114
        return $table->isQuoted()
614 38
            ? $this->quoteSingleIdentifier($identifierName)
615 114
            : $identifierName;
616
    }
617
618
    /**
619
     * {@inheritDoc}
620
     */
621 19
    public function getListTableForeignKeysSQL($table)
622
    {
623 19
        $table = $this->normalizeIdentifier($table);
624 19
        $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 19
      AND alc.table_name = " . $table . "
647
    ORDER BY cols.constraint_name ASC, cols.position ASC";
648
    }
649
650
    /**
651
     * {@inheritDoc}
652
     */
653 19
    public function getListTableConstraintsSQL($table)
654
    {
655 19
        $table = $this->normalizeIdentifier($table);
656 19
        $table = $this->quoteStringLiteral($table->getName());
657
658 19
        return "SELECT * FROM user_constraints WHERE table_name = " . $table;
659
    }
660
661
    /**
662
     * {@inheritDoc}
663
     */
664 95
    public function getListTableColumnsSQL($table, $database = null)
665
    {
666 95
        $table = $this->normalizeIdentifier($table);
667 95
        $table = $this->quoteStringLiteral($table->getName());
668
669 95
        $tabColumnsTableName = "user_tab_columns";
670 95
        $colCommentsTableName = "user_col_comments";
671 95
        $tabColumnsOwnerCondition = '';
672 95
        $colCommentsOwnerCondition = '';
673
674 95
        if (null !== $database && '/' !== $database) {
675 38
            $database = $this->normalizeIdentifier($database);
676 38
            $database = $this->quoteStringLiteral($database->getName());
677 38
            $tabColumnsTableName = "all_tab_columns";
678 38
            $colCommentsTableName = "all_col_comments";
679 38
            $tabColumnsOwnerCondition = "AND c.owner = " . $database;
680 38
            $colCommentsOwnerCondition = "AND d.OWNER = c.OWNER";
681
        }
682
683
        return "SELECT   c.*,
684
                         (
685
                             SELECT d.comments
686 95
                             FROM   $colCommentsTableName d
687 95
                             WHERE  d.TABLE_NAME = c.TABLE_NAME " . $colCommentsOwnerCondition . "
688
                             AND    d.COLUMN_NAME = c.COLUMN_NAME
689
                         ) AS comments
690 95
                FROM     $tabColumnsTableName c
691 95
                WHERE    c.table_name = " . $table . " $tabColumnsOwnerCondition
692
                ORDER BY c.column_id";
693
    }
694
695
    /**
696
     * {@inheritDoc}
697
     */
698 57
    public function getDropSequenceSQL($sequence)
699
    {
700 57
        if ($sequence instanceof Sequence) {
701
            $sequence = $sequence->getQuotedName($this);
702
        }
703
704 57
        return 'DROP SEQUENCE ' . $sequence;
705
    }
706
707
    /**
708
     * {@inheritDoc}
709
     */
710 38
    public function getDropForeignKeySQL($foreignKey, $table)
711
    {
712 38
        if (! $foreignKey instanceof ForeignKeyConstraint) {
713 19
            $foreignKey = new Identifier($foreignKey);
714
        }
715
716 38
        if (! $table instanceof Table) {
717 38
            $table = new Identifier($table);
718
        }
719
720 38
        $foreignKey = $foreignKey->getQuotedName($this);
721 38
        $table = $table->getQuotedName($this);
722
723 38
        return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey;
724
    }
725
726
    /**
727
     * {@inheritdoc}
728
     */
729 190
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
730
    {
731 190
        $referentialAction = null;
732
733 190
        if ($foreignKey->hasOption('onDelete')) {
734 76
            $referentialAction = $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
735
        }
736
737 190
        return $referentialAction ? ' ON DELETE ' . $referentialAction : '';
738
    }
739
740
    /**
741
     * {@inheritdoc}
742
     */
743 190
    public function getForeignKeyReferentialActionSQL($action)
744
    {
745 190
        $action = strtoupper($action);
746
747
        switch ($action) {
748 190
            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 152
            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 76
                return '';
753
754 114
            case 'CASCADE':
755 57
            case 'SET NULL':
756 95
                return $action;
757
758
            default:
759
                // SET DEFAULT is not supported, throw exception instead.
760 19
                throw new \InvalidArgumentException('Invalid foreign key action: ' . $action);
761
        }
762
    }
763
764
    /**
765
     * {@inheritDoc}
766
     */
767 19
    public function getDropDatabaseSQL($database)
768
    {
769 19
        return 'DROP USER ' . $database . ' CASCADE';
770
    }
771
772
    /**
773
     * {@inheritDoc}
774
     */
775 304
    public function getAlterTableSQL(TableDiff $diff)
776
    {
777 304
        $sql = [];
778 304
        $commentsSQL = [];
779 304
        $columnSql = [];
780
781 304
        $fields = [];
782
783 304
        foreach ($diff->addedColumns as $column) {
784 76
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
785
                continue;
786
            }
787
788 76
            $fields[] = $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
789 76
            if ($comment = $this->getColumnComment($column)) {
790 19
                $commentsSQL[] = $this->getCommentOnColumnSQL(
791 19
                    $diff->getName($this)->getQuotedName($this),
792 19
                    $column->getQuotedName($this),
793 76
                    $comment
794
                );
795
            }
796
        }
797
798 304
        if (count($fields)) {
799 76
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ADD (' . implode(', ', $fields) . ')';
800
        }
801
802 304
        $fields = [];
803 304
        foreach ($diff->changedColumns as $columnDiff) {
804 171
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
805
                continue;
806
            }
807
808
            /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */
809 171
            $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 171
            if ($column->getType() instanceof BinaryType &&
815 171
                $columnDiff->hasChanged('fixed') &&
816 171
                count($columnDiff->changedProperties) === 1
817
            ) {
818 19
                continue;
819
            }
820
821 152
            $columnHasChangedComment = $columnDiff->hasChanged('comment');
822
823
            /**
824
             * Do not add query part if only comment has changed
825
             */
826 152
            if ( ! ($columnHasChangedComment && count($columnDiff->changedProperties) === 1)) {
827 114
                $columnInfo = $column->toArray();
828
829 114
                if ( ! $columnDiff->hasChanged('notnull')) {
830 76
                    unset($columnInfo['notnull']);
831
                }
832
833 114
                $fields[] = $column->getQuotedName($this) . $this->getColumnDeclarationSQL('', $columnInfo);
834
            }
835
836 152
            if ($columnHasChangedComment) {
837 38
                $commentsSQL[] = $this->getCommentOnColumnSQL(
838 38
                    $diff->getName($this)->getQuotedName($this),
839 38
                    $column->getQuotedName($this),
840 152
                    $this->getColumnComment($column)
841
                );
842
            }
843
        }
844
845 304
        if (count($fields)) {
846 114
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' MODIFY (' . implode(', ', $fields) . ')';
847
        }
848
849 304
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
850 76
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
851
                continue;
852
            }
853
854 76
            $oldColumnName = new Identifier($oldColumnName);
855
856 76
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) .
857 76
                ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) .' TO ' . $column->getQuotedName($this);
858
        }
859
860 304
        $fields = [];
861 304
        foreach ($diff->removedColumns as $column) {
862 57
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
863
                continue;
864
            }
865
866 57
            $fields[] = $column->getQuotedName($this);
867
        }
868
869 304
        if (count($fields)) {
870 57
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' DROP (' . implode(', ', $fields).')';
871
        }
872
873 304
        $tableSql = [];
874
875 304
        if ( ! $this->onSchemaAlterTable($diff, $tableSql)) {
876 304
            $sql = array_merge($sql, $commentsSQL);
877
878 304
            if ($diff->newName !== false) {
879 38
                $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' RENAME TO ' . $diff->getNewName()->getQuotedName($this);
880
            }
881
882 304
            $sql = array_merge(
883 304
                $this->getPreAlterTableIndexForeignKeySQL($diff),
884 304
                $sql,
885 304
                $this->getPostAlterTableIndexForeignKeySQL($diff)
886
            );
887
        }
888
889 304
        return array_merge($sql, $tableSql, $columnSql);
890
    }
891
892
    /**
893
     * {@inheritdoc}
894
     */
895 361
    public function getColumnDeclarationSQL($name, array $field)
896
    {
897 361
        if (isset($field['columnDefinition'])) {
898 19
            $columnDef = $this->getCustomTypeDeclarationSQL($field);
899
        } else {
900 342
            $default = $this->getDefaultValueDeclarationSQL($field);
901
902 342
            $notnull = '';
903
904 342
            if (isset($field['notnull'])) {
905 304
                $notnull = $field['notnull'] ? ' NOT NULL' : ' NULL';
906
            }
907
908 342
            $unique = (isset($field['unique']) && $field['unique']) ?
909 342
                ' ' . $this->getUniqueFieldDeclarationSQL() : '';
910
911 342
            $check = (isset($field['check']) && $field['check']) ?
912 342
                ' ' . $field['check'] : '';
913
914 342
            $typeDecl = $field['type']->getSQLDeclaration($field, $this);
915 342
            $columnDef = $typeDecl . $default . $notnull . $unique . $check;
916
        }
917
918 361
        return $name . ' ' . $columnDef;
919
    }
920
921
    /**
922
     * {@inheritdoc}
923
     */
924 95
    protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
925
    {
926 95
        if (strpos($tableName, '.') !== false) {
927 38
            list($schema) = explode('.', $tableName);
928 38
            $oldIndexName = $schema . '.' . $oldIndexName;
929
        }
930
931 95
        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 19
    public function usesSequenceEmulatedIdentityColumns()
946
    {
947 19
        return true;
948
    }
949
950
    /**
951
     * {@inheritdoc}
952
     */
953 133
    public function getIdentitySequenceName($tableName, $columnName)
954
    {
955 133
        $table = new Identifier($tableName);
956
957
        // No usage of column name to preserve BC compatibility with <2.5
958 133
        $identitySequenceName = $table->getName() . '_SEQ';
959
960 133
        if ($table->isQuoted()) {
961 57
            $identitySequenceName = '"' . $identitySequenceName . '"';
962
        }
963
964 133
        $identitySequenceIdentifier = $this->normalizeIdentifier($identitySequenceName);
965
966 133
        return $identitySequenceIdentifier->getQuotedName($this);
967
    }
968
969
    /**
970
     * {@inheritDoc}
971
     */
972 228
    public function supportsCommentOnStatement()
973
    {
974 228
        return true;
975
    }
976
977
    /**
978
     * {@inheritDoc}
979
     */
980 38
    public function getName()
981
    {
982 38
        return 'oracle';
983
    }
984
985
    /**
986
     * {@inheritDoc}
987
     */
988 114
    protected function doModifyLimitQuery($query, $limit, $offset = null)
989
    {
990 114
        if ($limit === null && $offset === null) {
991
            return $query;
992
        }
993
994 114
        if (preg_match('/^\s*SELECT/i', $query)) {
995 114
            if (!preg_match('/\sFROM\s/i', $query)) {
996
                $query .= " FROM dual";
997
            }
998
999 114
            $columns = ['a.*'];
1000
1001 114
            if ($offset > 0) {
1002 38
                $columns[] = 'ROWNUM AS doctrine_rownum';
1003
            }
1004
1005 114
            $query = sprintf('SELECT %s FROM (%s) a', implode(', ', $columns), $query);
1006
1007 114
            if ($limit !== null) {
1008 95
                $query .= sprintf(' WHERE ROWNUM <= %d', $offset + $limit);
1009
            }
1010
1011 114
            if ($offset > 0) {
1012 38
                $query = sprintf('SELECT * FROM (%s) WHERE doctrine_rownum >= %d', $query, $offset + 1);
1013
            }
1014
        }
1015
1016 114
        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 19
    public function getTruncateTableSQL($tableName, $cascade = false)
1110
    {
1111 19
        $tableIdentifier = new Identifier($tableName);
1112
1113 19
        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 95
    protected function initializeDoctrineTypeMappings()
1130
    {
1131 95
        $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 95
    }
1157
1158
    /**
1159
     * {@inheritDoc}
1160
     */
1161
    public function releaseSavePoint($savepoint)
1162
    {
1163
        return '';
1164
    }
1165
1166
    /**
1167
     * {@inheritDoc}
1168
     */
1169 931
    protected function getReservedKeywordsClass()
1170
    {
1171 931
        return Keywords\OracleKeywords::class;
1172
    }
1173
1174
    /**
1175
     * {@inheritDoc}
1176
     */
1177 19
    public function getBlobTypeDeclarationSQL(array $field)
1178
    {
1179 19
        return 'BLOB';
1180
    }
1181
1182
    /**
1183
     * {@inheritdoc}
1184
     */
1185 323
    public function quoteStringLiteral($str)
1186
    {
1187 323
        $str = str_replace('\\', '\\\\', $str); // Oracle requires backslashes to be escaped aswell.
1188
1189 323
        return parent::quoteStringLiteral($str);
1190
    }
1191
}
1192