Completed
Push — develop ( a59880...a5109c )
by Sergei
112:22 queued 47:20
created

OraclePlatform::getGuidExpression()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 0
1
<?php
2
3
namespace Doctrine\DBAL\Platforms;
4
5
use Doctrine\DBAL\DBALException;
6
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
7
use Doctrine\DBAL\Schema\Identifier;
8
use Doctrine\DBAL\Schema\Index;
9
use Doctrine\DBAL\Schema\Sequence;
10
use Doctrine\DBAL\Schema\Table;
11
use Doctrine\DBAL\Schema\TableDiff;
12
use Doctrine\DBAL\TransactionIsolationLevel;
13
use Doctrine\DBAL\Types\BinaryType;
14
use function array_merge;
15
use function count;
16
use function explode;
17
use function func_get_arg;
18
use function func_num_args;
19
use function implode;
20
use function preg_match;
21
use function sprintf;
22
use function str_replace;
23
use function strlen;
24
use function strpos;
25
use function strtoupper;
26
use function substr;
27
28
/**
29
 * OraclePlatform.
30
 *
31
 * @since 2.0
32
 * @author Roman Borschel <[email protected]>
33
 * @author Lukas Smith <[email protected]> (PEAR MDB2 library)
34
 * @author Benjamin Eberlei <[email protected]>
35
 */
36
class OraclePlatform extends AbstractPlatform
37
{
38
    /**
39
     * Assertion for Oracle identifiers.
40
     *
41
     * @link http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements008.htm
42
     *
43
     * @param string $identifier
44
     *
45
     * @throws DBALException
46
     */
47
    public static function assertValidIdentifier($identifier)
48
    {
49
        if ( ! preg_match('(^(([a-zA-Z]{1}[a-zA-Z0-9_$#]{0,})|("[^"]+"))$)', $identifier)) {
50
            throw new DBALException("Invalid Oracle identifier");
51
        }
52
    }
53
54
    /**
55
     * {@inheritDoc}
56
     */
57
    public function getSubstringExpression($value, $position, $length = null)
58
    {
59
        if ($length !== null) {
60
            return "SUBSTR($value, $position, $length)";
61
        }
62
63
        return "SUBSTR($value, $position)";
64
    }
65
66
    /**
67
     * {@inheritDoc}
68
     */
69
    public function getNowExpression($type = 'timestamp')
70
    {
71
        switch ($type) {
72
            case 'date':
73
            case 'time':
74
            case 'timestamp':
75
            default:
76
                return 'TO_CHAR(CURRENT_TIMESTAMP, \'YYYY-MM-DD HH24:MI:SS\')';
77
        }
78
    }
79
80
    /**
81
     * {@inheritDoc}
82
     */
83
    public function getLocateExpression($str, $substr, $startPos = false)
84
    {
85
        if ($startPos == false) {
86
            return 'INSTR('.$str.', '.$substr.')';
87
        }
88
89
        return 'INSTR('.$str.', '.$substr.', '.$startPos.')';
90
    }
91
92
    /**
93
     * {@inheritdoc}
94
     */
95
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
96
    {
97
        switch ($unit) {
98
            case DateIntervalUnit::MONTH:
99
            case DateIntervalUnit::QUARTER:
100
            case DateIntervalUnit::YEAR:
101
                switch ($unit) {
102
                    case DateIntervalUnit::QUARTER:
103
                        $interval *= 3;
104
                        break;
105
106
                    case DateIntervalUnit::YEAR:
107
                        $interval *= 12;
108
                        break;
109
                }
110
111
                return 'ADD_MONTHS(' . $date . ', ' . $operator . $interval . ')';
112
113
            default:
114
                $calculationClause = '';
115
116
                switch ($unit) {
117
                    case DateIntervalUnit::SECOND:
118
                        $calculationClause = '/24/60/60';
119
                        break;
120
121
                    case DateIntervalUnit::MINUTE:
122
                        $calculationClause = '/24/60';
123
                        break;
124
125
                    case DateIntervalUnit::HOUR:
126
                        $calculationClause = '/24';
127
                        break;
128
129
                    case DateIntervalUnit::WEEK:
130
                        $calculationClause = '*7';
131
                        break;
132
                }
133
134
                return '(' . $date . $operator . $interval . $calculationClause . ')';
135
        }
136
    }
137
138
    /**
139
     * {@inheritDoc}
140
     */
141
    public function getDateDiffExpression($date1, $date2)
142
    {
143
        return sprintf('TRUNC(%s) - TRUNC(%s)', $date1, $date2);
144
    }
145
146
    /**
147
     * {@inheritDoc}
148
     */
149
    public function getBitAndComparisonExpression($value1, $value2)
150
    {
151
        return 'BITAND('.$value1 . ', ' . $value2 . ')';
152
    }
153
154
    /**
155
     * {@inheritDoc}
156
     */
157
    public function getBitOrComparisonExpression($value1, $value2)
158
    {
159
        return '(' . $value1 . '-' .
160
                $this->getBitAndComparisonExpression($value1, $value2)
161
                . '+' . $value2 . ')';
162
    }
163
164
    /**
165
     * {@inheritDoc}
166
     *
167
     * Need to specifiy minvalue, since start with is hidden in the system and MINVALUE <= START WITH.
168
     * Therefore we can use MINVALUE to be able to get a hint what START WITH was for later introspection
169
     * in {@see listSequences()}
170
     */
171
    public function getCreateSequenceSQL(Sequence $sequence)
172
    {
173
        return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) .
174
               ' START WITH ' . $sequence->getInitialValue() .
175
               ' MINVALUE ' . $sequence->getInitialValue() .
176
               ' INCREMENT BY ' . $sequence->getAllocationSize() .
177
               $this->getSequenceCacheSQL($sequence);
178
    }
179
180
    /**
181
     * {@inheritDoc}
182
     */
183
    public function getAlterSequenceSQL(Sequence $sequence)
184
    {
185
        return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) .
186
               ' INCREMENT BY ' . $sequence->getAllocationSize()
187
               . $this->getSequenceCacheSQL($sequence);
188
    }
189
190
    /**
191
     * Cache definition for sequences
192
     *
193
     * @param Sequence $sequence
194
     *
195
     * @return string
196
     */
197
    private function getSequenceCacheSQL(Sequence $sequence)
198
    {
199
        if ($sequence->getCache() === 0) {
200
            return ' NOCACHE';
201
        } else if ($sequence->getCache() === 1) {
202
            return ' NOCACHE';
203
        } else if ($sequence->getCache() > 1) {
204
            return ' CACHE ' . $sequence->getCache();
205
        }
206
207
        return '';
208
    }
209
210
    /**
211
     * {@inheritDoc}
212
     */
213
    public function getSequenceNextValSQL($sequenceName)
214
    {
215
        return 'SELECT ' . $sequenceName . '.nextval FROM DUAL';
216
    }
217
218
    /**
219
     * {@inheritDoc}
220
     */
221
    public function getSetTransactionIsolationSQL($level)
222
    {
223
        return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level);
224
    }
225
226
    /**
227
     * {@inheritDoc}
228
     */
229
    protected function _getTransactionIsolationLevelSQL($level)
230
    {
231
        switch ($level) {
232
            case TransactionIsolationLevel::READ_UNCOMMITTED:
233
                return 'READ UNCOMMITTED';
234
            case TransactionIsolationLevel::READ_COMMITTED:
235
                return 'READ COMMITTED';
236
            case TransactionIsolationLevel::REPEATABLE_READ:
237
            case TransactionIsolationLevel::SERIALIZABLE:
238
                return 'SERIALIZABLE';
239
            default:
240
                return parent::_getTransactionIsolationLevelSQL($level);
241
        }
242
    }
243
244
    /**
245
     * {@inheritDoc}
246
     */
247
    public function getBooleanTypeDeclarationSQL(array $field)
248
    {
249
        return 'NUMBER(1)';
250
    }
251
252
    /**
253
     * {@inheritDoc}
254
     */
255
    public function getIntegerTypeDeclarationSQL(array $field)
256
    {
257
        return 'NUMBER(10)';
258
    }
259
260
    /**
261
     * {@inheritDoc}
262
     */
263
    public function getBigIntTypeDeclarationSQL(array $field)
264
    {
265
        return 'NUMBER(20)';
266
    }
267
268
    /**
269
     * {@inheritDoc}
270
     */
271
    public function getSmallIntTypeDeclarationSQL(array $field)
272
    {
273
        return 'NUMBER(5)';
274
    }
275
276
    /**
277
     * {@inheritDoc}
278
     */
279
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
280
    {
281
        return 'TIMESTAMP(0)';
282
    }
283
284
    /**
285
     * {@inheritDoc}
286
     */
287
    public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration)
288
    {
289
        return 'TIMESTAMP(0) WITH TIME ZONE';
290
    }
291
292
    /**
293
     * {@inheritDoc}
294
     */
295
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
296
    {
297
        return 'DATE';
298
    }
299
300
    /**
301
     * {@inheritDoc}
302
     */
303
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
304
    {
305
        return 'DATE';
306
    }
307
308
    /**
309
     * {@inheritDoc}
310
     */
311
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
312
    {
313
        return '';
314
    }
315
316
    /**
317
     * {@inheritDoc}
318
     */
319
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
320
    {
321
        return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(2000)')
322
                : ($length ? 'VARCHAR2(' . $length . ')' : 'VARCHAR2(4000)');
323
    }
324
325
    /**
326
     * {@inheritdoc}
327
     */
328
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
329
    {
330
        return 'RAW(' . ($length ?: $this->getBinaryMaxLength()) . ')';
331
    }
332
333
    /**
334
     * {@inheritdoc}
335
     */
336
    public function getBinaryMaxLength()
337
    {
338
        return 2000;
339
    }
340
341
    /**
342
     * {@inheritDoc}
343
     */
344
    public function getClobTypeDeclarationSQL(array $field)
345
    {
346
        return 'CLOB';
347
    }
348
349
    /**
350
     * {@inheritDoc}
351
     */
352
    public function getListDatabasesSQL()
353
    {
354
        return 'SELECT username FROM all_users';
355
    }
356
357
    /**
358
     * {@inheritDoc}
359
     */
360
    public function getListSequencesSQL($database)
361
    {
362
        $database = $this->normalizeIdentifier($database);
363
        $database = $this->quoteStringLiteral($database->getName());
364
365
        return "SELECT sequence_name, min_value, increment_by FROM sys.all_sequences ".
366
               "WHERE SEQUENCE_OWNER = " . $database;
367
    }
368
369
    /**
370
     * {@inheritDoc}
371
     */
372
    protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
373
    {
374
        $indexes            = $options['indexes'] ?? [];
375
        $options['indexes'] = [];
376
        $sql                = parent::_getCreateTableSQL($tableName, $columns, $options);
377
378
        foreach ($columns as $name => $column) {
379
            if (isset($column['sequence'])) {
380
                $sql[] = $this->getCreateSequenceSQL($column['sequence']);
381
            }
382
383
            if (isset($column['autoincrement']) && $column['autoincrement'] ||
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (IssetNode && $column['a...e && $column['autoinc'], Probably Intended Meaning: IssetNode && ($column['a... && $column['autoinc'])
Loading history...
384
               (isset($column['autoinc']) && $column['autoinc'])) {
385
                $sql = array_merge($sql, $this->getCreateAutoincrementSql($name, $tableName));
386
            }
387
        }
388
389
        if (isset($indexes) && ! empty($indexes)) {
390
            foreach ($indexes as $index) {
391
                $sql[] = $this->getCreateIndexSQL($index, $tableName);
392
            }
393
        }
394
395
        return $sql;
396
    }
397
398
    /**
399
     * {@inheritDoc}
400
     *
401
     * @license New BSD License
402
     * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaOracleReader.html
403
     */
404
    public function getListTableIndexesSQL($table, $currentDatabase = null)
405
    {
406
        $table = $this->normalizeIdentifier($table);
407
        $table = $this->quoteStringLiteral($table->getName());
408
409
        return "SELECT uind_col.index_name AS name,
410
                       (
411
                           SELECT uind.index_type
412
                           FROM   user_indexes uind
413
                           WHERE  uind.index_name = uind_col.index_name
414
                       ) AS type,
415
                       decode(
416
                           (
417
                               SELECT uind.uniqueness
418
                               FROM   user_indexes uind
419
                               WHERE  uind.index_name = uind_col.index_name
420
                           ),
421
                           'NONUNIQUE',
422
                           0,
423
                           'UNIQUE',
424
                           1
425
                       ) AS is_unique,
426
                       uind_col.column_name AS column_name,
427
                       uind_col.column_position AS column_pos,
428
                       (
429
                           SELECT ucon.constraint_type
430
                           FROM   user_constraints ucon
431
                           WHERE  ucon.index_name = uind_col.index_name
432
                       ) AS is_primary
433
             FROM      user_ind_columns uind_col
434
             WHERE     uind_col.table_name = " . $table . "
435
             ORDER BY  uind_col.column_position ASC";
436
    }
437
438
    /**
439
     * {@inheritDoc}
440
     */
441
    public function getListTablesSQL()
442
    {
443
        return 'SELECT * FROM sys.user_tables';
444
    }
445
446
    /**
447
     * {@inheritDoc}
448
     */
449
    public function getListViewsSQL($database)
450
    {
451
        return 'SELECT view_name, text FROM sys.user_views';
452
    }
453
454
    /**
455
     * {@inheritDoc}
456
     */
457
    public function getCreateViewSQL($name, $sql)
458
    {
459
        return 'CREATE VIEW ' . $name . ' AS ' . $sql;
460
    }
461
462
    /**
463
     * {@inheritDoc}
464
     */
465
    public function getDropViewSQL($name)
466
    {
467
        return 'DROP VIEW '. $name;
468
    }
469
470
    /**
471
     * @param string $name
472
     * @param string $table
473
     * @param int    $start
474
     *
475
     * @return array
476
     */
477
    public function getCreateAutoincrementSql($name, $table, $start = 1)
478
    {
479
        $tableIdentifier = $this->normalizeIdentifier($table);
480
        $quotedTableName = $tableIdentifier->getQuotedName($this);
481
        $unquotedTableName = $tableIdentifier->getName();
482
483
        $nameIdentifier = $this->normalizeIdentifier($name);
484
        $quotedName = $nameIdentifier->getQuotedName($this);
485
        $unquotedName = $nameIdentifier->getName();
486
487
        $sql = [];
488
489
        $autoincrementIdentifierName = $this->getAutoincrementIdentifierName($tableIdentifier);
490
491
        $idx = new Index($autoincrementIdentifierName, [$quotedName], true, true);
492
493
        $sql[] = 'DECLARE
494
  constraints_Count NUMBER;
495
BEGIN
496
  SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count FROM USER_CONSTRAINTS WHERE TABLE_NAME = \'' . $unquotedTableName . '\' AND CONSTRAINT_TYPE = \'P\';
497
  IF constraints_Count = 0 OR constraints_Count = \'\' THEN
498
    EXECUTE IMMEDIATE \''.$this->getCreateConstraintSQL($idx, $quotedTableName).'\';
499
  END IF;
500
END;';
501
502
        $sequenceName = $this->getIdentitySequenceName(
503
            $tableIdentifier->isQuoted() ? $quotedTableName : $unquotedTableName,
504
            $nameIdentifier->isQuoted() ? $quotedName : $unquotedName
505
        );
506
        $sequence = new Sequence($sequenceName, $start);
507
        $sql[] = $this->getCreateSequenceSQL($sequence);
508
509
        $sql[] = 'CREATE TRIGGER ' . $autoincrementIdentifierName . '
510
   BEFORE INSERT
511
   ON ' . $quotedTableName . '
512
   FOR EACH ROW
513
DECLARE
514
   last_Sequence NUMBER;
515
   last_InsertID NUMBER;
516
BEGIN
517
   SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL;
518
   IF (:NEW.' . $quotedName . ' IS NULL OR :NEW.'.$quotedName.' = 0) THEN
519
      SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL;
520
   ELSE
521
      SELECT NVL(Last_Number, 0) INTO last_Sequence
522
        FROM User_Sequences
523
       WHERE Sequence_Name = \'' . $sequence->getName() . '\';
524
      SELECT :NEW.' . $quotedName . ' INTO last_InsertID FROM DUAL;
525
      WHILE (last_InsertID > last_Sequence) LOOP
526
         SELECT ' . $sequenceName . '.NEXTVAL INTO last_Sequence FROM DUAL;
527
      END LOOP;
528
   END IF;
529
END;';
530
531
        return $sql;
532
    }
533
534
    /**
535
     * Returns the SQL statements to drop the autoincrement for the given table name.
536
     *
537
     * @param string $table The table name to drop the autoincrement for.
538
     *
539
     * @return array
540
     */
541
    public function getDropAutoincrementSql($table)
542
    {
543
        $table = $this->normalizeIdentifier($table);
544
        $autoincrementIdentifierName = $this->getAutoincrementIdentifierName($table);
545
        $identitySequenceName = $this->getIdentitySequenceName(
546
            $table->isQuoted() ? $table->getQuotedName($this) : $table->getName(),
547
            ''
548
        );
549
550
        return [
551
            'DROP TRIGGER ' . $autoincrementIdentifierName,
552
            $this->getDropSequenceSQL($identitySequenceName),
553
            $this->getDropConstraintSQL($autoincrementIdentifierName, $table->getQuotedName($this)),
554
        ];
555
    }
556
557
    /**
558
     * Normalizes the given identifier.
559
     *
560
     * Uppercases the given identifier if it is not quoted by intention
561
     * to reflect Oracle's internal auto uppercasing strategy of unquoted identifiers.
562
     *
563
     * @param string $name The identifier to normalize.
564
     *
565
     * @return Identifier The normalized identifier.
566
     */
567
    private function normalizeIdentifier($name)
568
    {
569
        $identifier = new Identifier($name);
570
571
        return $identifier->isQuoted() ? $identifier : new Identifier(strtoupper($name));
572
    }
573
574
    /**
575
     * Returns the autoincrement primary key identifier name for the given table identifier.
576
     *
577
     * Quotes the autoincrement primary key identifier name
578
     * if the given table name is quoted by intention.
579
     *
580
     * @param Identifier $table The table identifier to return the autoincrement primary key identifier name for.
581
     *
582
     * @return string
583
     */
584
    private function getAutoincrementIdentifierName(Identifier $table)
585
    {
586
        $identifierName = $table->getName() . '_AI_PK';
587
588
        return $table->isQuoted()
589
            ? $this->quoteSingleIdentifier($identifierName)
590
            : $identifierName;
591
    }
592
593
    /**
594
     * {@inheritDoc}
595
     */
596
    public function getListTableForeignKeysSQL($table)
597
    {
598
        $table = $this->normalizeIdentifier($table);
599
        $table = $this->quoteStringLiteral($table->getName());
600
601
        return "SELECT alc.constraint_name,
602
          alc.DELETE_RULE,
603
          cols.column_name \"local_column\",
604
          cols.position,
605
          (
606
              SELECT r_cols.table_name
607
              FROM   user_cons_columns r_cols
608
              WHERE  alc.r_constraint_name = r_cols.constraint_name
609
              AND    r_cols.position = cols.position
610
          ) AS \"references_table\",
611
          (
612
              SELECT r_cols.column_name
613
              FROM   user_cons_columns r_cols
614
              WHERE  alc.r_constraint_name = r_cols.constraint_name
615
              AND    r_cols.position = cols.position
616
          ) AS \"foreign_column\"
617
     FROM user_cons_columns cols
618
     JOIN user_constraints alc
619
       ON alc.constraint_name = cols.constraint_name
620
      AND alc.constraint_type = 'R'
621
      AND alc.table_name = " . $table . "
622
    ORDER BY cols.constraint_name ASC, cols.position ASC";
623
    }
624
625
    /**
626
     * {@inheritDoc}
627
     */
628
    public function getListTableConstraintsSQL($table)
629
    {
630
        $table = $this->normalizeIdentifier($table);
631
        $table = $this->quoteStringLiteral($table->getName());
632
633
        return "SELECT * FROM user_constraints WHERE table_name = " . $table;
634
    }
635
636
    /**
637
     * {@inheritDoc}
638
     */
639
    public function getListTableColumnsSQL($table, $database = null)
640
    {
641
        $table = $this->normalizeIdentifier($table);
642
        $table = $this->quoteStringLiteral($table->getName());
643
644
        $tabColumnsTableName = "user_tab_columns";
645
        $colCommentsTableName = "user_col_comments";
646
        $tabColumnsOwnerCondition = '';
647
        $colCommentsOwnerCondition = '';
648
649
        if (null !== $database && '/' !== $database) {
650
            $database = $this->normalizeIdentifier($database);
651
            $database = $this->quoteStringLiteral($database->getName());
652
            $tabColumnsTableName = "all_tab_columns";
653
            $colCommentsTableName = "all_col_comments";
654
            $tabColumnsOwnerCondition = "AND c.owner = " . $database;
655
            $colCommentsOwnerCondition = "AND d.OWNER = c.OWNER";
656
        }
657
658
        return "SELECT   c.*,
659
                         (
660
                             SELECT d.comments
661
                             FROM   $colCommentsTableName d
662
                             WHERE  d.TABLE_NAME = c.TABLE_NAME " . $colCommentsOwnerCondition . "
663
                             AND    d.COLUMN_NAME = c.COLUMN_NAME
664
                         ) AS comments
665
                FROM     $tabColumnsTableName c
666
                WHERE    c.table_name = " . $table . " $tabColumnsOwnerCondition
667
                ORDER BY c.column_id";
668
    }
669
670
    /**
671
     * {@inheritDoc}
672
     */
673
    public function getDropSequenceSQL($sequence)
674
    {
675
        if ($sequence instanceof Sequence) {
676
            $sequence = $sequence->getQuotedName($this);
677
        }
678
679
        return 'DROP SEQUENCE ' . $sequence;
680
    }
681
682
    /**
683
     * {@inheritDoc}
684
     */
685
    public function getDropForeignKeySQL($foreignKey, $table)
686
    {
687
        if (! $foreignKey instanceof ForeignKeyConstraint) {
688
            $foreignKey = new Identifier($foreignKey);
689
        }
690
691
        if (! $table instanceof Table) {
692
            $table = new Identifier($table);
693
        }
694
695
        $foreignKey = $foreignKey->getQuotedName($this);
696
        $table = $table->getQuotedName($this);
697
698
        return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey;
699
    }
700
701
    /**
702
     * {@inheritdoc}
703
     */
704
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
705
    {
706
        $referentialAction = null;
707
708
        if ($foreignKey->hasOption('onDelete')) {
709
            $referentialAction = $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
710
        }
711
712
        return $referentialAction ? ' ON DELETE ' . $referentialAction : '';
713
    }
714
715
    /**
716
     * {@inheritdoc}
717
     */
718
    public function getForeignKeyReferentialActionSQL($action)
719
    {
720
        $action = strtoupper($action);
721
722
        switch ($action) {
723
            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...
724
            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...
725
                // NO ACTION cannot be declared explicitly,
726
                // therefore returning empty string to indicate to OMIT the referential clause.
727
                return '';
728
729
            case 'CASCADE':
730
            case 'SET NULL':
731
                return $action;
732
733
            default:
734
                // SET DEFAULT is not supported, throw exception instead.
735
                throw new \InvalidArgumentException('Invalid foreign key action: ' . $action);
736
        }
737
    }
738
739
    /**
740
     * {@inheritDoc}
741
     */
742
    public function getDropDatabaseSQL($database)
743
    {
744
        return 'DROP USER ' . $database . ' CASCADE';
745
    }
746
747
    /**
748
     * {@inheritDoc}
749
     */
750
    public function getAlterTableSQL(TableDiff $diff)
751
    {
752
        $sql = [];
753
        $commentsSQL = [];
754
        $columnSql = [];
755
756
        $fields = [];
757
758
        foreach ($diff->addedColumns as $column) {
759
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
760
                continue;
761
            }
762
763
            $fields[] = $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
764
            if ($comment = $this->getColumnComment($column)) {
765
                $commentsSQL[] = $this->getCommentOnColumnSQL(
766
                    $diff->getName($this)->getQuotedName($this),
767
                    $column->getQuotedName($this),
768
                    $comment
769
                );
770
            }
771
        }
772
773
        if (count($fields)) {
774
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ADD (' . implode(', ', $fields) . ')';
775
        }
776
777
        $fields = [];
778
        foreach ($diff->changedColumns as $columnDiff) {
779
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
780
                continue;
781
            }
782
783
            /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */
784
            $column = $columnDiff->column;
785
786
            // Do not generate column alteration clause if type is binary and only fixed property has changed.
787
            // Oracle only supports binary type columns with variable length.
788
            // Avoids unnecessary table alteration statements.
789
            if ($column->getType() instanceof BinaryType &&
790
                $columnDiff->hasChanged('fixed') &&
791
                count($columnDiff->changedProperties) === 1
792
            ) {
793
                continue;
794
            }
795
796
            $columnHasChangedComment = $columnDiff->hasChanged('comment');
797
798
            /**
799
             * Do not add query part if only comment has changed
800
             */
801
            if ( ! ($columnHasChangedComment && count($columnDiff->changedProperties) === 1)) {
802
                $columnInfo = $column->toArray();
803
804
                if ( ! $columnDiff->hasChanged('notnull')) {
805
                    unset($columnInfo['notnull']);
806
                }
807
808
                $fields[] = $column->getQuotedName($this) . $this->getColumnDeclarationSQL('', $columnInfo);
809
            }
810
811
            if ($columnHasChangedComment) {
812
                $commentsSQL[] = $this->getCommentOnColumnSQL(
813
                    $diff->getName($this)->getQuotedName($this),
814
                    $column->getQuotedName($this),
815
                    $this->getColumnComment($column)
816
                );
817
            }
818
        }
819
820
        if (count($fields)) {
821
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' MODIFY (' . implode(', ', $fields) . ')';
822
        }
823
824
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
825
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
826
                continue;
827
            }
828
829
            $oldColumnName = new Identifier($oldColumnName);
830
831
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) .
832
                ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) .' TO ' . $column->getQuotedName($this);
833
        }
834
835
        $fields = [];
836
        foreach ($diff->removedColumns as $column) {
837
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
838
                continue;
839
            }
840
841
            $fields[] = $column->getQuotedName($this);
842
        }
843
844
        if (count($fields)) {
845
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' DROP (' . implode(', ', $fields).')';
846
        }
847
848
        $tableSql = [];
849
850
        if ( ! $this->onSchemaAlterTable($diff, $tableSql)) {
851
            $sql = array_merge($sql, $commentsSQL);
852
853
            if ($diff->newName !== false) {
854
                $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' RENAME TO ' . $diff->getNewName()->getQuotedName($this);
855
            }
856
857
            $sql = array_merge(
858
                $this->getPreAlterTableIndexForeignKeySQL($diff),
859
                $sql,
860
                $this->getPostAlterTableIndexForeignKeySQL($diff)
861
            );
862
        }
863
864
        return array_merge($sql, $tableSql, $columnSql);
865
    }
866
867
    /**
868
     * {@inheritdoc}
869
     */
870
    public function getColumnDeclarationSQL($name, array $field)
871
    {
872
        if (isset($field['columnDefinition'])) {
873
            $columnDef = $this->getCustomTypeDeclarationSQL($field);
874
        } else {
875
            $default = $this->getDefaultValueDeclarationSQL($field);
876
877
            $notnull = '';
878
879
            if (isset($field['notnull'])) {
880
                $notnull = $field['notnull'] ? ' NOT NULL' : ' NULL';
881
            }
882
883
            $unique = (isset($field['unique']) && $field['unique']) ?
884
                ' ' . $this->getUniqueFieldDeclarationSQL() : '';
885
886
            $check = (isset($field['check']) && $field['check']) ?
887
                ' ' . $field['check'] : '';
888
889
            $typeDecl = $field['type']->getSQLDeclaration($field, $this);
890
            $columnDef = $typeDecl . $default . $notnull . $unique . $check;
891
        }
892
893
        return $name . ' ' . $columnDef;
894
    }
895
896
    /**
897
     * {@inheritdoc}
898
     */
899
    protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
900
    {
901
        if (strpos($tableName, '.') !== false) {
902
            list($schema) = explode('.', $tableName);
903
            $oldIndexName = $schema . '.' . $oldIndexName;
904
        }
905
906
        return ['ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)];
907
    }
908
909
    /**
910
     * {@inheritDoc}
911
     */
912
    public function prefersSequences()
913
    {
914
        return true;
915
    }
916
917
    /**
918
     * {@inheritdoc}
919
     */
920
    public function usesSequenceEmulatedIdentityColumns()
921
    {
922
        return true;
923
    }
924
925
    /**
926
     * {@inheritdoc}
927
     */
928
    public function getIdentitySequenceName($tableName, $columnName)
929
    {
930
        $table = new Identifier($tableName);
931
932
        // No usage of column name to preserve BC compatibility with <2.5
933
        $identitySequenceName = $table->getName() . '_SEQ';
934
935
        if ($table->isQuoted()) {
936
            $identitySequenceName = '"' . $identitySequenceName . '"';
937
        }
938
939
        $identitySequenceIdentifier = $this->normalizeIdentifier($identitySequenceName);
940
941
        return $identitySequenceIdentifier->getQuotedName($this);
942
    }
943
944
    /**
945
     * {@inheritDoc}
946
     */
947
    public function supportsCommentOnStatement()
948
    {
949
        return true;
950
    }
951
952
    /**
953
     * {@inheritDoc}
954
     */
955
    public function getName()
956
    {
957
        return 'oracle';
958
    }
959
960
    /**
961
     * {@inheritDoc}
962
     */
963
    protected function doModifyLimitQuery($query, $limit, $offset = null)
964
    {
965
        if ($limit === null && $offset <= 0) {
966
            return $query;
967
        }
968
969
        if (preg_match('/^\s*SELECT/i', $query)) {
970
            if (!preg_match('/\sFROM\s/i', $query)) {
971
                $query .= " FROM dual";
972
            }
973
974
            $columns = ['a.*'];
975
976
            if ($offset > 0) {
977
                $columns[] = 'ROWNUM AS doctrine_rownum';
978
            }
979
980
            $query = sprintf('SELECT %s FROM (%s) a', implode(', ', $columns), $query);
981
982
            if ($limit !== null) {
983
                $query .= sprintf(' WHERE ROWNUM <= %d', $offset + $limit);
984
            }
985
986
            if ($offset > 0) {
987
                $query = sprintf('SELECT * FROM (%s) WHERE doctrine_rownum >= %d', $query, $offset + 1);
988
            }
989
        }
990
991
        return $query;
992
    }
993
994
    /**
995
     * {@inheritDoc}
996
     *
997
     * Oracle returns all column names in SQL result sets in uppercase.
998
     */
999
    public function getSQLResultCasing($column)
1000
    {
1001
        return strtoupper($column);
1002
    }
1003
1004
    /**
1005
     * {@inheritDoc}
1006
     */
1007
    public function getCreateTemporaryTableSnippetSQL()
1008
    {
1009
        return "CREATE GLOBAL TEMPORARY TABLE";
1010
    }
1011
1012
    /**
1013
     * {@inheritDoc}
1014
     */
1015
    public function getDateTimeTzFormatString()
1016
    {
1017
        return 'Y-m-d H:i:sP';
1018
    }
1019
1020
    /**
1021
     * {@inheritDoc}
1022
     */
1023
    public function getDateFormatString()
1024
    {
1025
        return 'Y-m-d 00:00:00';
1026
    }
1027
1028
    /**
1029
     * {@inheritDoc}
1030
     */
1031
    public function getTimeFormatString()
1032
    {
1033
        return '1900-01-01 H:i:s';
1034
    }
1035
1036
    /**
1037
     * {@inheritDoc}
1038
     */
1039
    public function fixSchemaElementName($schemaElementName)
1040
    {
1041
        if (strlen($schemaElementName) > 30) {
1042
            // Trim it
1043
            return substr($schemaElementName, 0, 30);
1044
        }
1045
1046
        return $schemaElementName;
1047
    }
1048
1049
    /**
1050
     * {@inheritDoc}
1051
     */
1052
    public function getMaxIdentifierLength()
1053
    {
1054
        return 30;
1055
    }
1056
1057
    /**
1058
     * {@inheritDoc}
1059
     */
1060
    public function supportsSequences()
1061
    {
1062
        return true;
1063
    }
1064
1065
    /**
1066
     * {@inheritDoc}
1067
     */
1068
    public function supportsForeignKeyOnUpdate()
1069
    {
1070
        return false;
1071
    }
1072
1073
    /**
1074
     * {@inheritDoc}
1075
     */
1076
    public function supportsReleaseSavepoints()
1077
    {
1078
        return false;
1079
    }
1080
1081
    /**
1082
     * {@inheritDoc}
1083
     */
1084
    public function getTruncateTableSQL($tableName, $cascade = false)
1085
    {
1086
        $tableIdentifier = new Identifier($tableName);
1087
1088
        return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this);
1089
    }
1090
1091
    /**
1092
     * {@inheritDoc}
1093
     */
1094
    public function getDummySelectSQL()
1095
    {
1096
        $expression = func_num_args() > 0 ? func_get_arg(0) : '1';
1097
1098
        return sprintf('SELECT %s FROM DUAL', $expression);
1099
    }
1100
1101
    /**
1102
     * {@inheritDoc}
1103
     */
1104
    protected function initializeDoctrineTypeMappings()
1105
    {
1106
        $this->doctrineTypeMapping = [
1107
            'binary_double'  => 'float',
1108
            'binary_float'   => 'float',
1109
            'binary_integer' => 'boolean',
1110
            'blob'           => 'blob',
1111
            'char'           => 'string',
1112
            'clob'           => 'text',
1113
            'date'           => 'date',
1114
            'float'          => 'float',
1115
            'integer'        => 'integer',
1116
            'long'           => 'string',
1117
            'long raw'       => 'blob',
1118
            'nchar'          => 'string',
1119
            'nclob'          => 'text',
1120
            'number'         => 'integer',
1121
            'nvarchar2'      => 'string',
1122
            'pls_integer'    => 'boolean',
1123
            'raw'            => 'binary',
1124
            'rowid'          => 'string',
1125
            'timestamp'      => 'datetime',
1126
            'timestamptz'    => 'datetimetz',
1127
            'urowid'         => 'string',
1128
            'varchar'        => 'string',
1129
            'varchar2'       => 'string',
1130
        ];
1131
    }
1132
1133
    /**
1134
     * {@inheritDoc}
1135
     */
1136
    public function releaseSavePoint($savepoint)
1137
    {
1138
        return '';
1139
    }
1140
1141
    /**
1142
     * {@inheritDoc}
1143
     */
1144
    protected function getReservedKeywordsClass()
1145
    {
1146
        return Keywords\OracleKeywords::class;
1147
    }
1148
1149
    /**
1150
     * {@inheritDoc}
1151
     */
1152
    public function getBlobTypeDeclarationSQL(array $field)
1153
    {
1154
        return 'BLOB';
1155
    }
1156
1157
    /**
1158
     * {@inheritdoc}
1159
     */
1160
    public function quoteStringLiteral($str)
1161
    {
1162
        $str = str_replace('\\', '\\\\', $str); // Oracle requires backslashes to be escaped aswell.
1163
1164
        return parent::quoteStringLiteral($str);
1165
    }
1166
}
1167