Completed
Pull Request — master (#3212)
by Sergei
49:12 queued 45:02
created

OraclePlatform::getBitAndComparisonExpression()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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