Failed Conditions
Push — exceptions ( 7b5f2f...d1ce0d )
by Michael
24:32
created

OraclePlatform   F

Complexity

Total Complexity 164

Size/Duplication

Total Lines 1137
Duplicated Lines 0 %

Test Coverage

Coverage 72.58%

Importance

Changes 0
Metric Value
wmc 164
dl 0
loc 1137
ccs 278
cts 383
cp 0.7258
rs 0.6314
c 0
b 0
f 0

74 Methods

Rating   Name   Duplication   Size   Complexity  
A getReservedKeywordsClass() 0 3 1
A quoteStringLiteral() 0 5 1
B initializeDoctrineTypeMappings() 0 26 1
A getBlobTypeDeclarationSQL() 0 3 1
A releaseSavePoint() 0 3 1
A usesSequenceEmulatedIdentityColumns() 0 3 1
B getForeignKeyReferentialActionSQL() 0 18 5
A fixSchemaElementName() 0 8 2
F getAlterTableSQL() 0 115 22
A getBooleanTypeDeclarationSQL() 0 3 1
B getListTableForeignKeysSQL() 0 26 1
A getSequenceNextValSQL() 0 3 1
A supportsCommentOnStatement() 0 3 1
B getListTableColumnsSQL() 0 28 3
A getDropViewSQL() 0 3 1
A getBinaryTypeDeclarationSQLSnippet() 0 3 2
A getDropAutoincrementSql() 0 13 2
A getAutoincrementIdentifierName() 0 7 2
A getBigIntTypeDeclarationSQL() 0 3 1
A getIdentitySequenceName() 0 14 2
A supportsSequences() 0 3 1
A getAdvancedForeignKeyOptionsSQL() 0 9 3
B _getTransactionIsolationLevelSQL() 0 12 5
A getSQLResultCasing() 0 3 1
A getDateDiffExpression() 0 3 1
A getVarcharTypeDeclarationSQLSnippet() 0 4 4
A getAlterSequenceSQL() 0 5 1
A getDateFormatString() 0 3 1
A getGuidExpression() 0 3 1
A getTimeFormatString() 0 3 1
C getColumnDeclarationSQL() 0 24 8
A getListTablesSQL() 0 3 1
B getListTableIndexesSQL() 0 31 1
A supportsForeignKeyOnUpdate() 0 3 1
A getTruncateTableSQL() 0 5 1
A getDummySelectSQL() 0 5 2
A getBitAndComparisonExpression() 0 3 1
A getName() 0 3 1
A getBinaryMaxLength() 0 3 1
A getCreateSequenceSQL() 0 7 1
A getDateTypeDeclarationSQL() 0 3 1
A getSetTransactionIsolationSQL() 0 3 1
A getRenameIndexSQL() 0 8 2
A getCreateTemporaryTableSnippetSQL() 0 3 1
A getDateTimeTzFormatString() 0 3 1
A getClobTypeDeclarationSQL() 0 3 1
A prefersSequences() 0 3 1
A getDateTimeTzTypeDeclarationSQL() 0 3 1
A _getCommonIntegerTypeDeclarationSQL() 0 3 1
A getCreateViewSQL() 0 3 1
A getListDatabasesSQL() 0 3 1
A getLocateExpression() 0 7 2
A getTimeTypeDeclarationSQL() 0 3 1
A getListSequencesSQL() 0 7 1
A getDropForeignKeySQL() 0 14 3
A normalizeIdentifier() 0 5 2
A getDateTimeTypeDeclarationSQL() 0 3 1
C doModifyLimitQuery() 0 29 8
C _getCreateTableSQL() 0 24 10
A assertValidIdentifier() 0 4 2
A getMaxIdentifierLength() 0 3 1
A getSmallIntTypeDeclarationSQL() 0 3 1
A getSequenceCacheSQL() 0 11 4
A getDropDatabaseSQL() 0 3 1
A supportsReleaseSavepoints() 0 3 1
D getDateArithmeticIntervalExpression() 0 40 10
A getSubstringExpression() 0 7 2
A getDropSequenceSQL() 0 7 2
A getCreateAutoincrementSql() 0 55 3
A getListTableConstraintsSQL() 0 6 1
A getIntegerTypeDeclarationSQL() 0 3 1
A getNowExpression() 0 8 4
A getListViewsSQL() 0 3 1
A getBitOrComparisonExpression() 0 5 1

How to fix   Complexity   

Complex Class

Complex classes like OraclePlatform often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use OraclePlatform, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Doctrine\DBAL\Platforms;
4
5
use Doctrine\DBAL\DBALException;
6
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
7
use Doctrine\DBAL\Schema\Identifier;
8
use Doctrine\DBAL\Schema\Index;
9
use Doctrine\DBAL\Schema\Sequence;
10
use Doctrine\DBAL\Schema\Table;
11
use Doctrine\DBAL\Schema\TableDiff;
12
use Doctrine\DBAL\TransactionIsolationLevel;
13
use Doctrine\DBAL\Types\BinaryType;
14
use function array_merge;
15
use function count;
16
use function explode;
17
use function func_get_arg;
18
use function func_num_args;
19
use function implode;
20
use function preg_match;
21
use function sprintf;
22
use function str_replace;
23
use function strlen;
24
use function strpos;
25
use function strtoupper;
26
use function substr;
27
28
/**
29
 * OraclePlatform.
30
 *
31
 * @since 2.0
32
 * @author Roman Borschel <[email protected]>
33
 * @author Lukas Smith <[email protected]> (PEAR MDB2 library)
34
 * @author Benjamin Eberlei <[email protected]>
35
 */
36
class OraclePlatform extends AbstractPlatform
37
{
38
    /**
39
     * Assertion for Oracle identifiers.
40
     *
41
     * @link http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements008.htm
42
     *
43
     * @param string $identifier
44
     *
45
     * @throws DBALException
46
     */
47 224
    public static function assertValidIdentifier($identifier)
48
    {
49 224
        if ( ! preg_match('(^(([a-zA-Z]{1}[a-zA-Z0-9_$#]{0,})|("[^"]+"))$)', $identifier)) {
50 80
            throw new DBALException("Invalid Oracle identifier");
51
        }
52 144
    }
53
54
    /**
55
     * {@inheritDoc}
56
     */
57
    public function getSubstringExpression($value, $position, $length = null)
58
    {
59
        if ($length !== null) {
60
            return "SUBSTR($value, $position, $length)";
61
        }
62
63
        return "SUBSTR($value, $position)";
64
    }
65
66
    /**
67
     * {@inheritDoc}
68
     */
69
    public function getNowExpression($type = 'timestamp')
70
    {
71
        switch ($type) {
72
            case 'date':
73
            case 'time':
74
            case 'timestamp':
75
            default:
76
                return 'TO_CHAR(CURRENT_TIMESTAMP, \'YYYY-MM-DD HH24:MI:SS\')';
77
        }
78
    }
79
80
    /**
81
     * {@inheritDoc}
82
     */
83
    public function getLocateExpression($str, $substr, $startPos = false)
84
    {
85
        if ($startPos == false) {
86
            return 'INSTR('.$str.', '.$substr.')';
87
        }
88
89
        return 'INSTR('.$str.', '.$substr.', '.$startPos.')';
90
    }
91
92
    /**
93
     * {@inheritDoc}
94
     */
95
    public function getGuidExpression()
96
    {
97
        return 'SYS_GUID()';
98
    }
99
100
    /**
101
     * {@inheritdoc}
102
     */
103
    protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
104
    {
105
        switch ($unit) {
106
            case DateIntervalUnit::MONTH:
107
            case DateIntervalUnit::QUARTER:
108
            case DateIntervalUnit::YEAR:
109
                switch ($unit) {
110
                    case DateIntervalUnit::QUARTER:
111
                        $interval *= 3;
112
                        break;
113
114
                    case DateIntervalUnit::YEAR:
115
                        $interval *= 12;
116
                        break;
117
                }
118
119
                return 'ADD_MONTHS(' . $date . ', ' . $operator . $interval . ')';
120
121
            default:
122
                $calculationClause = '';
123
124
                switch ($unit) {
125
                    case DateIntervalUnit::SECOND:
126
                        $calculationClause = '/24/60/60';
127
                        break;
128
129
                    case DateIntervalUnit::MINUTE:
130
                        $calculationClause = '/24/60';
131
                        break;
132
133
                    case DateIntervalUnit::HOUR:
134
                        $calculationClause = '/24';
135
                        break;
136
137
                    case DateIntervalUnit::WEEK:
138
                        $calculationClause = '*7';
139
                        break;
140
                }
141
142
                return '(' . $date . $operator . $interval . $calculationClause . ')';
143
        }
144
    }
145
146
    /**
147
     * {@inheritDoc}
148
     */
149
    public function getDateDiffExpression($date1, $date2)
150
    {
151
        return sprintf('TRUNC(%s) - TRUNC(%s)', $date1, $date2);
152
    }
153
154
    /**
155
     * {@inheritDoc}
156
     */
157 32
    public function getBitAndComparisonExpression($value1, $value2)
158
    {
159 32
        return 'BITAND('.$value1 . ', ' . $value2 . ')';
160
    }
161
162
    /**
163
     * {@inheritDoc}
164
     */
165 16
    public function getBitOrComparisonExpression($value1, $value2)
166
    {
167 16
        return '(' . $value1 . '-' .
168 16
                $this->getBitAndComparisonExpression($value1, $value2)
169 16
                . '+' . $value2 . ')';
170
    }
171
172
    /**
173
     * {@inheritDoc}
174
     *
175
     * Need to specifiy minvalue, since start with is hidden in the system and MINVALUE <= START WITH.
176
     * Therefore we can use MINVALUE to be able to get a hint what START WITH was for later introspection
177
     * in {@see listSequences()}
178
     */
179 96
    public function getCreateSequenceSQL(Sequence $sequence)
180
    {
181 96
        return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) .
182 96
               ' START WITH ' . $sequence->getInitialValue() .
183 96
               ' MINVALUE ' . $sequence->getInitialValue() .
184 96
               ' INCREMENT BY ' . $sequence->getAllocationSize() .
185 96
               $this->getSequenceCacheSQL($sequence);
186
    }
187
188
    /**
189
     * {@inheritDoc}
190
     */
191
    public function getAlterSequenceSQL(Sequence $sequence)
192
    {
193
        return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) .
194
               ' INCREMENT BY ' . $sequence->getAllocationSize()
195
               . $this->getSequenceCacheSQL($sequence);
196
    }
197
198
    /**
199
     * Cache definition for sequences
200
     *
201
     * @param Sequence $sequence
202
     *
203
     * @return string
204
     */
205 96
    private function getSequenceCacheSQL(Sequence $sequence)
206
    {
207 96
        if ($sequence->getCache() === 0) {
208 16
            return ' NOCACHE';
209 80
        } else if ($sequence->getCache() === 1) {
210 16
            return ' NOCACHE';
211 64
        } else if ($sequence->getCache() > 1) {
212 16
            return ' CACHE ' . $sequence->getCache();
213
        }
214
215 48
        return '';
216
    }
217
218
    /**
219
     * {@inheritDoc}
220
     */
221
    public function getSequenceNextValSQL($sequenceName)
222
    {
223
        return 'SELECT ' . $sequenceName . '.nextval FROM DUAL';
224
    }
225
226
    /**
227
     * {@inheritDoc}
228
     */
229 16
    public function getSetTransactionIsolationSQL($level)
230
    {
231 16
        return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level);
232
    }
233
234
    /**
235
     * {@inheritDoc}
236
     */
237 16
    protected function _getTransactionIsolationLevelSQL($level)
238
    {
239 16
        switch ($level) {
240
            case TransactionIsolationLevel::READ_UNCOMMITTED:
241 16
                return 'READ UNCOMMITTED';
242
            case TransactionIsolationLevel::READ_COMMITTED:
243 16
                return 'READ COMMITTED';
244
            case TransactionIsolationLevel::REPEATABLE_READ:
245
            case TransactionIsolationLevel::SERIALIZABLE:
246 16
                return 'SERIALIZABLE';
247
            default:
248
                return parent::_getTransactionIsolationLevelSQL($level);
249
        }
250
    }
251
252
    /**
253
     * {@inheritDoc}
254
     */
255 16
    public function getBooleanTypeDeclarationSQL(array $field)
256
    {
257 16
        return 'NUMBER(1)';
258
    }
259
260
    /**
261
     * {@inheritDoc}
262
     */
263 160
    public function getIntegerTypeDeclarationSQL(array $field)
264
    {
265 160
        return 'NUMBER(10)';
266
    }
267
268
    /**
269
     * {@inheritDoc}
270
     */
271
    public function getBigIntTypeDeclarationSQL(array $field)
272
    {
273
        return 'NUMBER(20)';
274
    }
275
276
    /**
277
     * {@inheritDoc}
278
     */
279
    public function getSmallIntTypeDeclarationSQL(array $field)
280
    {
281
        return 'NUMBER(5)';
282
    }
283
284
    /**
285
     * {@inheritDoc}
286
     */
287
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
288
    {
289
        return 'TIMESTAMP(0)';
290
    }
291
292
    /**
293
     * {@inheritDoc}
294
     */
295
    public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration)
296
    {
297
        return 'TIMESTAMP(0) WITH TIME ZONE';
298
    }
299
300
    /**
301
     * {@inheritDoc}
302
     */
303
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
304
    {
305
        return 'DATE';
306
    }
307
308
    /**
309
     * {@inheritDoc}
310
     */
311
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
312
    {
313
        return 'DATE';
314
    }
315
316
    /**
317
     * {@inheritDoc}
318
     */
319
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
320
    {
321
        return '';
322
    }
323
324
    /**
325
     * {@inheritDoc}
326
     */
327 224
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
328
    {
329 224
        return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(2000)')
330 224
                : ($length ? 'VARCHAR2(' . $length . ')' : 'VARCHAR2(4000)');
331
    }
332
333
    /**
334
     * {@inheritdoc}
335
     */
336 16
    protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
337
    {
338 16
        return 'RAW(' . ($length ?: $this->getBinaryMaxLength()) . ')';
339
    }
340
341
    /**
342
     * {@inheritdoc}
343
     */
344 32
    public function getBinaryMaxLength()
345
    {
346 32
        return 2000;
347
    }
348
349
    /**
350
     * {@inheritDoc}
351
     */
352 32
    public function getClobTypeDeclarationSQL(array $field)
353
    {
354 32
        return 'CLOB';
355
    }
356
357
    /**
358
     * {@inheritDoc}
359
     */
360
    public function getListDatabasesSQL()
361
    {
362
        return 'SELECT username FROM all_users';
363
    }
364
365
    /**
366
     * {@inheritDoc}
367
     */
368 16
    public function getListSequencesSQL($database)
369
    {
370 16
        $database = $this->normalizeIdentifier($database);
371 16
        $database = $this->quoteStringLiteral($database->getName());
372
373
        return "SELECT sequence_name, min_value, increment_by FROM sys.all_sequences ".
374 16
               "WHERE SEQUENCE_OWNER = " . $database;
375
    }
376
377
    /**
378
     * {@inheritDoc}
379
     */
380 176
    protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
381
    {
382 176
        $indexes            = $options['indexes'] ?? [];
383 176
        $options['indexes'] = [];
384 176
        $sql                = parent::_getCreateTableSQL($tableName, $columns, $options);
385
386 176
        foreach ($columns as $name => $column) {
387 176
            if (isset($column['sequence'])) {
388
                $sql[] = $this->getCreateSequenceSQL($column['sequence'], 1);
0 ignored issues
show
Unused Code introduced by
The call to Doctrine\DBAL\Platforms\...:getCreateSequenceSQL() has too many arguments starting with 1. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

388
                /** @scrutinizer ignore-call */ 
389
                $sql[] = $this->getCreateSequenceSQL($column['sequence'], 1);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
389
            }
390
391 176
            if (isset($column['autoincrement']) && $column['autoincrement'] ||
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: {currentAssign}, Probably Intended Meaning: {alternativeAssign}
Loading history...
392 176
               (isset($column['autoinc']) && $column['autoinc'])) {
393 176
                $sql = array_merge($sql, $this->getCreateAutoincrementSql($name, $tableName));
394
            }
395
        }
396
397 176
        if (isset($indexes) && ! empty($indexes)) {
398 48
            foreach ($indexes as $index) {
399 48
                $sql[] = $this->getCreateIndexSQL($index, $tableName);
400
            }
401
        }
402
403 176
        return $sql;
404
    }
405
406
    /**
407
     * {@inheritDoc}
408
     *
409
     * @license New BSD License
410
     * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaOracleReader.html
411
     */
412 16
    public function getListTableIndexesSQL($table, $currentDatabase = null)
413
    {
414 16
        $table = $this->normalizeIdentifier($table);
415 16
        $table = $this->quoteStringLiteral($table->getName());
416
417
        return "SELECT uind_col.index_name AS name,
418
                       (
419
                           SELECT uind.index_type
420
                           FROM   user_indexes uind
421
                           WHERE  uind.index_name = uind_col.index_name
422
                       ) AS type,
423
                       decode(
424
                           (
425
                               SELECT uind.uniqueness
426
                               FROM   user_indexes uind
427
                               WHERE  uind.index_name = uind_col.index_name
428
                           ),
429
                           'NONUNIQUE',
430
                           0,
431
                           'UNIQUE',
432
                           1
433
                       ) AS is_unique,
434
                       uind_col.column_name AS column_name,
435
                       uind_col.column_position AS column_pos,
436
                       (
437
                           SELECT ucon.constraint_type
438
                           FROM   user_constraints ucon
439
                           WHERE  ucon.index_name = uind_col.index_name
440
                       ) AS is_primary
441
             FROM      user_ind_columns uind_col
442 16
             WHERE     uind_col.table_name = " . $table . "
443
             ORDER BY  uind_col.column_position ASC";
444
    }
445
446
    /**
447
     * {@inheritDoc}
448
     */
449
    public function getListTablesSQL()
450
    {
451
        return 'SELECT * FROM sys.user_tables';
452
    }
453
454
    /**
455
     * {@inheritDoc}
456
     */
457
    public function getListViewsSQL($database)
458
    {
459
        return 'SELECT view_name, text FROM sys.user_views';
460
    }
461
462
    /**
463
     * {@inheritDoc}
464
     */
465
    public function getCreateViewSQL($name, $sql)
466
    {
467
        return 'CREATE VIEW ' . $name . ' AS ' . $sql;
468
    }
469
470
    /**
471
     * {@inheritDoc}
472
     */
473
    public function getDropViewSQL($name)
474
    {
475
        return 'DROP VIEW '. $name;
476
    }
477
478
    /**
479
     * @param string $name
480
     * @param string $table
481
     * @param int    $start
482
     *
483
     * @return array
484
     */
485 48
    public function getCreateAutoincrementSql($name, $table, $start = 1)
486
    {
487 48
        $tableIdentifier = $this->normalizeIdentifier($table);
488 48
        $quotedTableName = $tableIdentifier->getQuotedName($this);
489 48
        $unquotedTableName = $tableIdentifier->getName();
490
491 48
        $nameIdentifier = $this->normalizeIdentifier($name);
492 48
        $quotedName = $nameIdentifier->getQuotedName($this);
493 48
        $unquotedName = $nameIdentifier->getName();
494
495 48
        $sql = [];
496
497 48
        $autoincrementIdentifierName = $this->getAutoincrementIdentifierName($tableIdentifier);
498
499 48
        $idx = new Index($autoincrementIdentifierName, [$quotedName], true, true);
500
501 48
        $sql[] = 'DECLARE
502
  constraints_Count NUMBER;
503
BEGIN
504 48
  SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count FROM USER_CONSTRAINTS WHERE TABLE_NAME = \'' . $unquotedTableName . '\' AND CONSTRAINT_TYPE = \'P\';
505
  IF constraints_Count = 0 OR constraints_Count = \'\' THEN
506 48
    EXECUTE IMMEDIATE \''.$this->getCreateConstraintSQL($idx, $quotedTableName).'\';
507
  END IF;
508
END;';
509
510 48
        $sequenceName = $this->getIdentitySequenceName(
511 48
            $tableIdentifier->isQuoted() ? $quotedTableName : $unquotedTableName,
512 48
            $nameIdentifier->isQuoted() ? $quotedName : $unquotedName
513
        );
514 48
        $sequence = new Sequence($sequenceName, $start);
515 48
        $sql[] = $this->getCreateSequenceSQL($sequence);
516
517 48
        $sql[] = 'CREATE TRIGGER ' . $autoincrementIdentifierName . '
518
   BEFORE INSERT
519 48
   ON ' . $quotedTableName . '
520
   FOR EACH ROW
521
DECLARE
522
   last_Sequence NUMBER;
523
   last_InsertID NUMBER;
524
BEGIN
525 48
   SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL;
526 48
   IF (:NEW.' . $quotedName . ' IS NULL OR :NEW.'.$quotedName.' = 0) THEN
527 48
      SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL;
528
   ELSE
529
      SELECT NVL(Last_Number, 0) INTO last_Sequence
530
        FROM User_Sequences
531 48
       WHERE Sequence_Name = \'' . $sequence->getName() . '\';
532 48
      SELECT :NEW.' . $quotedName . ' INTO last_InsertID FROM DUAL;
533
      WHILE (last_InsertID > last_Sequence) LOOP
534 48
         SELECT ' . $sequenceName . '.NEXTVAL INTO last_Sequence FROM DUAL;
535
      END LOOP;
536
   END IF;
537
END;';
538
539 48
        return $sql;
540
    }
541
542
    /**
543
     * Returns the SQL statements to drop the autoincrement for the given table name.
544
     *
545
     * @param string $table The table name to drop the autoincrement for.
546
     *
547
     * @return array
548
     */
549 48
    public function getDropAutoincrementSql($table)
550
    {
551 48
        $table = $this->normalizeIdentifier($table);
552 48
        $autoincrementIdentifierName = $this->getAutoincrementIdentifierName($table);
553 48
        $identitySequenceName = $this->getIdentitySequenceName(
554 48
            $table->isQuoted() ? $table->getQuotedName($this) : $table->getName(),
555 48
            ''
556
        );
557
558
        return [
559 48
            'DROP TRIGGER ' . $autoincrementIdentifierName,
560 48
            $this->getDropSequenceSQL($identitySequenceName),
561 48
            $this->getDropConstraintSQL($autoincrementIdentifierName, $table->getQuotedName($this)),
562
        ];
563
    }
564
565
    /**
566
     * Normalizes the given identifier.
567
     *
568
     * Uppercases the given identifier if it is not quoted by intention
569
     * to reflect Oracle's internal auto uppercasing strategy of unquoted identifiers.
570
     *
571
     * @param string $name The identifier to normalize.
572
     *
573
     * @return Identifier The normalized identifier.
574
     */
575 256
    private function normalizeIdentifier($name)
576
    {
577 256
        $identifier = new Identifier($name);
578
579 256
        return $identifier->isQuoted() ? $identifier : new Identifier(strtoupper($name));
580
    }
581
582
    /**
583
     * Returns the autoincrement primary key identifier name for the given table identifier.
584
     *
585
     * Quotes the autoincrement primary key identifier name
586
     * if the given table name is quoted by intention.
587
     *
588
     * @param Identifier $table The table identifier to return the autoincrement primary key identifier name for.
589
     *
590
     * @return string
591
     */
592 96
    private function getAutoincrementIdentifierName(Identifier $table)
593
    {
594 96
        $identifierName = $table->getName() . '_AI_PK';
595
596 96
        return $table->isQuoted()
597 32
            ? $this->quoteSingleIdentifier($identifierName)
598 96
            : $identifierName;
599
    }
600
601
    /**
602
     * {@inheritDoc}
603
     */
604 16
    public function getListTableForeignKeysSQL($table)
605
    {
606 16
        $table = $this->normalizeIdentifier($table);
607 16
        $table = $this->quoteStringLiteral($table->getName());
608
609
        return "SELECT alc.constraint_name,
610
          alc.DELETE_RULE,
611
          cols.column_name \"local_column\",
612
          cols.position,
613
          (
614
              SELECT r_cols.table_name
615
              FROM   user_cons_columns r_cols
616
              WHERE  alc.r_constraint_name = r_cols.constraint_name
617
              AND    r_cols.position = cols.position
618
          ) AS \"references_table\",
619
          (
620
              SELECT r_cols.column_name
621
              FROM   user_cons_columns r_cols
622
              WHERE  alc.r_constraint_name = r_cols.constraint_name
623
              AND    r_cols.position = cols.position
624
          ) AS \"foreign_column\"
625
     FROM user_cons_columns cols
626
     JOIN user_constraints alc
627
       ON alc.constraint_name = cols.constraint_name
628
      AND alc.constraint_type = 'R'
629 16
      AND alc.table_name = " . $table . "
630
    ORDER BY cols.constraint_name ASC, cols.position ASC";
631
    }
632
633
    /**
634
     * {@inheritDoc}
635
     */
636 16
    public function getListTableConstraintsSQL($table)
637
    {
638 16
        $table = $this->normalizeIdentifier($table);
639 16
        $table = $this->quoteStringLiteral($table->getName());
640
641 16
        return "SELECT * FROM user_constraints WHERE table_name = " . $table;
642
    }
643
644
    /**
645
     * {@inheritDoc}
646
     */
647 80
    public function getListTableColumnsSQL($table, $database = null)
648
    {
649 80
        $table = $this->normalizeIdentifier($table);
650 80
        $table = $this->quoteStringLiteral($table->getName());
651
652 80
        $tabColumnsTableName = "user_tab_columns";
653 80
        $colCommentsTableName = "user_col_comments";
654 80
        $tabColumnsOwnerCondition = '';
655 80
        $colCommentsOwnerCondition = '';
656
657 80
        if (null !== $database && '/' !== $database) {
658 32
            $database = $this->normalizeIdentifier($database);
659 32
            $database = $this->quoteStringLiteral($database->getName());
660 32
            $tabColumnsTableName = "all_tab_columns";
661 32
            $colCommentsTableName = "all_col_comments";
662 32
            $tabColumnsOwnerCondition = "AND c.owner = " . $database;
663 32
            $colCommentsOwnerCondition = "AND d.OWNER = c.OWNER";
664
        }
665
666
        return "SELECT   c.*,
667
                         (
668
                             SELECT d.comments
669 80
                             FROM   $colCommentsTableName d
670 80
                             WHERE  d.TABLE_NAME = c.TABLE_NAME " . $colCommentsOwnerCondition . "
671
                             AND    d.COLUMN_NAME = c.COLUMN_NAME
672
                         ) AS comments
673 80
                FROM     $tabColumnsTableName c
674 80
                WHERE    c.table_name = " . $table . " $tabColumnsOwnerCondition
675
                ORDER BY c.column_id";
676
    }
677
678
    /**
679
     * {@inheritDoc}
680
     */
681 48
    public function getDropSequenceSQL($sequence)
682
    {
683 48
        if ($sequence instanceof Sequence) {
684
            $sequence = $sequence->getQuotedName($this);
685
        }
686
687 48
        return 'DROP SEQUENCE ' . $sequence;
688
    }
689
690
    /**
691
     * {@inheritDoc}
692
     */
693 32
    public function getDropForeignKeySQL($foreignKey, $table)
694
    {
695 32
        if (! $foreignKey instanceof ForeignKeyConstraint) {
696 16
            $foreignKey = new Identifier($foreignKey);
697
        }
698
699 32
        if (! $table instanceof Table) {
700 32
            $table = new Identifier($table);
701
        }
702
703 32
        $foreignKey = $foreignKey->getQuotedName($this);
704 32
        $table = $table->getQuotedName($this);
705
706 32
        return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey;
707
    }
708
709
    /**
710
     * {@inheritdoc}
711
     */
712 160
    public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
713
    {
714 160
        $referentialAction = null;
715
716 160
        if ($foreignKey->hasOption('onDelete')) {
717 64
            $referentialAction = $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
718
        }
719
720 160
        return $referentialAction ? ' ON DELETE ' . $referentialAction : '';
721
    }
722
723
    /**
724
     * {@inheritdoc}
725
     */
726 160
    public function getForeignKeyReferentialActionSQL($action)
727
    {
728 160
        $action = strtoupper($action);
729
730 160
        switch ($action) {
731 160
            case 'RESTRICT': // RESTRICT is not supported, therefore falling back to NO ACTION.
732 128
            case 'NO ACTION':
733
                // NO ACTION cannot be declared explicitly,
734
                // therefore returning empty string to indicate to OMIT the referential clause.
735 64
                return '';
736
737 96
            case 'CASCADE':
738 48
            case 'SET NULL':
739 80
                return $action;
740
741
            default:
742
                // SET DEFAULT is not supported, throw exception instead.
743 16
                throw new \InvalidArgumentException('Invalid foreign key action: ' . $action);
744
        }
745
    }
746
747
    /**
748
     * {@inheritDoc}
749
     */
750 16
    public function getDropDatabaseSQL($database)
751
    {
752 16
        return 'DROP USER ' . $database . ' CASCADE';
753
    }
754
755
    /**
756
     * {@inheritDoc}
757
     */
758 256
    public function getAlterTableSQL(TableDiff $diff)
759
    {
760 256
        $sql = [];
761 256
        $commentsSQL = [];
762 256
        $columnSql = [];
763
764 256
        $fields = [];
765
766 256
        foreach ($diff->addedColumns as $column) {
767 64
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
768
                continue;
769
            }
770
771 64
            $fields[] = $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
772 64
            if ($comment = $this->getColumnComment($column)) {
773 16
                $commentsSQL[] = $this->getCommentOnColumnSQL(
774 16
                    $diff->getName($this)->getQuotedName($this),
775 16
                    $column->getQuotedName($this),
776 64
                    $comment
777
                );
778
            }
779
        }
780
781 256
        if (count($fields)) {
782 64
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ADD (' . implode(', ', $fields) . ')';
783
        }
784
785 256
        $fields = [];
786 256
        foreach ($diff->changedColumns as $columnDiff) {
787 144
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
788
                continue;
789
            }
790
791
            /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */
792 144
            $column = $columnDiff->column;
793
794
            // Do not generate column alteration clause if type is binary and only fixed property has changed.
795
            // Oracle only supports binary type columns with variable length.
796
            // Avoids unnecessary table alteration statements.
797 144
            if ($column->getType() instanceof BinaryType &&
798 144
                $columnDiff->hasChanged('fixed') &&
799 144
                count($columnDiff->changedProperties) === 1
800
            ) {
801 16
                continue;
802
            }
803
804 128
            $columnHasChangedComment = $columnDiff->hasChanged('comment');
805
806
            /**
807
             * Do not add query part if only comment has changed
808
             */
809 128
            if ( ! ($columnHasChangedComment && count($columnDiff->changedProperties) === 1)) {
810 96
                $columnInfo = $column->toArray();
811
812 96
                if ( ! $columnDiff->hasChanged('notnull')) {
813 64
                    unset($columnInfo['notnull']);
814
                }
815
816 96
                $fields[] = $column->getQuotedName($this) . $this->getColumnDeclarationSQL('', $columnInfo);
817
            }
818
819 128
            if ($columnHasChangedComment) {
820 32
                $commentsSQL[] = $this->getCommentOnColumnSQL(
821 32
                    $diff->getName($this)->getQuotedName($this),
822 32
                    $column->getQuotedName($this),
823 128
                    $this->getColumnComment($column)
824
                );
825
            }
826
        }
827
828 256
        if (count($fields)) {
829 96
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' MODIFY (' . implode(', ', $fields) . ')';
830
        }
831
832 256
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
833 64
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
834
                continue;
835
            }
836
837 64
            $oldColumnName = new Identifier($oldColumnName);
838
839 64
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) .
840 64
                ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) .' TO ' . $column->getQuotedName($this);
841
        }
842
843 256
        $fields = [];
844 256
        foreach ($diff->removedColumns as $column) {
845 48
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
846
                continue;
847
            }
848
849 48
            $fields[] = $column->getQuotedName($this);
850
        }
851
852 256
        if (count($fields)) {
853 48
            $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' DROP (' . implode(', ', $fields).')';
854
        }
855
856 256
        $tableSql = [];
857
858 256
        if ( ! $this->onSchemaAlterTable($diff, $tableSql)) {
859 256
            $sql = array_merge($sql, $commentsSQL);
860
861 256
            if ($diff->newName !== false) {
862 32
                $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' RENAME TO ' . $diff->getNewName()->getQuotedName($this);
863
            }
864
865 256
            $sql = array_merge(
866 256
                $this->getPreAlterTableIndexForeignKeySQL($diff),
867 256
                $sql,
868 256
                $this->getPostAlterTableIndexForeignKeySQL($diff)
869
            );
870
        }
871
872 256
        return array_merge($sql, $tableSql, $columnSql);
873
    }
874
875
    /**
876
     * {@inheritdoc}
877
     */
878 304
    public function getColumnDeclarationSQL($name, array $field)
879
    {
880 304
        if (isset($field['columnDefinition'])) {
881 16
            $columnDef = $this->getCustomTypeDeclarationSQL($field);
882
        } else {
883 288
            $default = $this->getDefaultValueDeclarationSQL($field);
884
885 288
            $notnull = '';
886
887 288
            if (isset($field['notnull'])) {
888 256
                $notnull = $field['notnull'] ? ' NOT NULL' : ' NULL';
889
            }
890
891 288
            $unique = (isset($field['unique']) && $field['unique']) ?
892 288
                ' ' . $this->getUniqueFieldDeclarationSQL() : '';
893
894 288
            $check = (isset($field['check']) && $field['check']) ?
895 288
                ' ' . $field['check'] : '';
896
897 288
            $typeDecl = $field['type']->getSQLDeclaration($field, $this);
898 288
            $columnDef = $typeDecl . $default . $notnull . $unique . $check;
899
        }
900
901 304
        return $name . ' ' . $columnDef;
902
    }
903
904
    /**
905
     * {@inheritdoc}
906
     */
907 80
    protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
908
    {
909 80
        if (strpos($tableName, '.') !== false) {
910 32
            list($schema) = explode('.', $tableName);
911 32
            $oldIndexName = $schema . '.' . $oldIndexName;
912
        }
913
914 80
        return ['ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)];
915
    }
916
917
    /**
918
     * {@inheritDoc}
919
     */
920
    public function prefersSequences()
921
    {
922
        return true;
923
    }
924
925
    /**
926
     * {@inheritdoc}
927
     */
928 16
    public function usesSequenceEmulatedIdentityColumns()
929
    {
930 16
        return true;
931
    }
932
933
    /**
934
     * {@inheritdoc}
935
     */
936 112
    public function getIdentitySequenceName($tableName, $columnName)
937
    {
938 112
        $table = new Identifier($tableName);
939
940
        // No usage of column name to preserve BC compatibility with <2.5
941 112
        $identitySequenceName = $table->getName() . '_SEQ';
942
943 112
        if ($table->isQuoted()) {
944 48
            $identitySequenceName = '"' . $identitySequenceName . '"';
945
        }
946
947 112
        $identitySequenceIdentifier = $this->normalizeIdentifier($identitySequenceName);
948
949 112
        return $identitySequenceIdentifier->getQuotedName($this);
950
    }
951
952
    /**
953
     * {@inheritDoc}
954
     */
955 192
    public function supportsCommentOnStatement()
956
    {
957 192
        return true;
958
    }
959
960
    /**
961
     * {@inheritDoc}
962
     */
963 32
    public function getName()
964
    {
965 32
        return 'oracle';
966
    }
967
968
    /**
969
     * {@inheritDoc}
970
     */
971 96
    protected function doModifyLimitQuery($query, $limit, $offset = null)
972
    {
973 96
        if ($limit === null && $offset === null) {
974
            return $query;
975
        }
976
977 96
        if (preg_match('/^\s*SELECT/i', $query)) {
978 96
            if (!preg_match('/\sFROM\s/i', $query)) {
979
                $query .= " FROM dual";
980
            }
981
982 96
            $columns = ['a.*'];
983
984 96
            if ($offset > 0) {
985 32
                $columns[] = 'ROWNUM AS doctrine_rownum';
986
            }
987
988 96
            $query = sprintf('SELECT %s FROM (%s) a', implode(', ', $columns), $query);
989
990 96
            if ($limit !== null) {
991 80
                $query .= sprintf(' WHERE ROWNUM <= %d', $offset + $limit);
992
            }
993
994 96
            if ($offset > 0) {
995 32
                $query = sprintf('SELECT * FROM (%s) WHERE doctrine_rownum >= %d', $query, $offset + 1);
996
            }
997
        }
998
999 96
        return $query;
1000
    }
1001
1002
    /**
1003
     * {@inheritDoc}
1004
     *
1005
     * Oracle returns all column names in SQL result sets in uppercase.
1006
     */
1007
    public function getSQLResultCasing($column)
1008
    {
1009
        return strtoupper($column);
1010
    }
1011
1012
    /**
1013
     * {@inheritDoc}
1014
     */
1015
    public function getCreateTemporaryTableSnippetSQL()
1016
    {
1017
        return "CREATE GLOBAL TEMPORARY TABLE";
1018
    }
1019
1020
    /**
1021
     * {@inheritDoc}
1022
     */
1023
    public function getDateTimeTzFormatString()
1024
    {
1025
        return 'Y-m-d H:i:sP';
1026
    }
1027
1028
    /**
1029
     * {@inheritDoc}
1030
     */
1031
    public function getDateFormatString()
1032
    {
1033
        return 'Y-m-d 00:00:00';
1034
    }
1035
1036
    /**
1037
     * {@inheritDoc}
1038
     */
1039
    public function getTimeFormatString()
1040
    {
1041
        return '1900-01-01 H:i:s';
1042
    }
1043
1044
    /**
1045
     * {@inheritDoc}
1046
     */
1047
    public function fixSchemaElementName($schemaElementName)
1048
    {
1049
        if (strlen($schemaElementName) > 30) {
1050
            // Trim it
1051
            return substr($schemaElementName, 0, 30);
1052
        }
1053
1054
        return $schemaElementName;
1055
    }
1056
1057
    /**
1058
     * {@inheritDoc}
1059
     */
1060
    public function getMaxIdentifierLength()
1061
    {
1062
        return 30;
1063
    }
1064
1065
    /**
1066
     * {@inheritDoc}
1067
     */
1068
    public function supportsSequences()
1069
    {
1070
        return true;
1071
    }
1072
1073
    /**
1074
     * {@inheritDoc}
1075
     */
1076
    public function supportsForeignKeyOnUpdate()
1077
    {
1078
        return false;
1079
    }
1080
1081
    /**
1082
     * {@inheritDoc}
1083
     */
1084
    public function supportsReleaseSavepoints()
1085
    {
1086
        return false;
1087
    }
1088
1089
    /**
1090
     * {@inheritDoc}
1091
     */
1092 16
    public function getTruncateTableSQL($tableName, $cascade = false)
1093
    {
1094 16
        $tableIdentifier = new Identifier($tableName);
1095
1096 16
        return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this);
1097
    }
1098
1099
    /**
1100
     * {@inheritDoc}
1101
     */
1102
    public function getDummySelectSQL()
1103
    {
1104
        $expression = func_num_args() > 0 ? func_get_arg(0) : '1';
1105
1106
        return sprintf('SELECT %s FROM DUAL', $expression);
1107
    }
1108
1109
    /**
1110
     * {@inheritDoc}
1111
     */
1112 80
    protected function initializeDoctrineTypeMappings()
1113
    {
1114 80
        $this->doctrineTypeMapping = [
1115
            'binary_double'  => 'float',
1116
            'binary_float'   => 'float',
1117
            'binary_integer' => 'boolean',
1118
            'blob'           => 'blob',
1119
            'char'           => 'string',
1120
            'clob'           => 'text',
1121
            'date'           => 'date',
1122
            'float'          => 'float',
1123
            'integer'        => 'integer',
1124
            'long'           => 'string',
1125
            'long raw'       => 'blob',
1126
            'nchar'          => 'string',
1127
            'nclob'          => 'text',
1128
            'number'         => 'integer',
1129
            'nvarchar2'      => 'string',
1130
            'pls_integer'    => 'boolean',
1131
            'raw'            => 'binary',
1132
            'rowid'          => 'string',
1133
            'timestamp'      => 'datetime',
1134
            'timestamptz'    => 'datetimetz',
1135
            'urowid'         => 'string',
1136
            'varchar'        => 'string',
1137
            'varchar2'       => 'string',
1138
        ];
1139 80
    }
1140
1141
    /**
1142
     * {@inheritDoc}
1143
     */
1144
    public function releaseSavePoint($savepoint)
1145
    {
1146
        return '';
1147
    }
1148
1149
    /**
1150
     * {@inheritDoc}
1151
     */
1152 784
    protected function getReservedKeywordsClass()
1153
    {
1154 784
        return Keywords\OracleKeywords::class;
1155
    }
1156
1157
    /**
1158
     * {@inheritDoc}
1159
     */
1160 16
    public function getBlobTypeDeclarationSQL(array $field)
1161
    {
1162 16
        return 'BLOB';
1163
    }
1164
1165
    /**
1166
     * {@inheritdoc}
1167
     */
1168 272
    public function quoteStringLiteral($str)
1169
    {
1170 272
        $str = str_replace('\\', '\\\\', $str); // Oracle requires backslashes to be escaped aswell.
1171
1172 272
        return parent::quoteStringLiteral($str);
1173
    }
1174
}
1175