Completed
Push — 2.8 ( c0195c...8d2399 )
by Sergei
21:38
created

OraclePlatform   F

Complexity

Total Complexity 163

Size/Duplication

Total Lines 1129
Duplicated Lines 0 %

Test Coverage

Coverage 89.74%

Importance

Changes 0
Metric Value
wmc 163
eloc 351
dl 0
loc 1129
ccs 350
cts 390
cp 0.8974
rs 2
c 0
b 0
f 0

73 Methods

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