Completed
Push — master ( 15a9ef...af81c1 )
by Sergei
26:08
created

OraclePlatform::getAdvancedForeignKeyOptionsSQL()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

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