Completed
Push — master ( 3e2bee...8fa2d7 )
by Sergei
15:16 queued 07:05
created

getCreateTemporaryTableSnippetSQL()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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