Failed Conditions
Push — master ( a5d3e3...20bdb9 )
by Marco
13s
created

lib/Doctrine/DBAL/Platforms/OraclePlatform.php (2 issues)

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