diff options
Diffstat (limited to 'vendor/doctrine/dbal/src/Platforms/OraclePlatform.php')
-rw-r--r-- | vendor/doctrine/dbal/src/Platforms/OraclePlatform.php | 784 |
1 files changed, 784 insertions, 0 deletions
diff --git a/vendor/doctrine/dbal/src/Platforms/OraclePlatform.php b/vendor/doctrine/dbal/src/Platforms/OraclePlatform.php new file mode 100644 index 0000000..314f6ee --- /dev/null +++ b/vendor/doctrine/dbal/src/Platforms/OraclePlatform.php | |||
@@ -0,0 +1,784 @@ | |||
1 | <?php | ||
2 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\DBAL\Platforms; | ||
6 | |||
7 | use Doctrine\DBAL\Connection; | ||
8 | use Doctrine\DBAL\Exception\InvalidColumnType\ColumnLengthRequired; | ||
9 | use Doctrine\DBAL\Platforms\Keywords\KeywordList; | ||
10 | use Doctrine\DBAL\Platforms\Keywords\OracleKeywords; | ||
11 | use Doctrine\DBAL\Schema\ForeignKeyConstraint; | ||
12 | use Doctrine\DBAL\Schema\Identifier; | ||
13 | use Doctrine\DBAL\Schema\Index; | ||
14 | use Doctrine\DBAL\Schema\OracleSchemaManager; | ||
15 | use Doctrine\DBAL\Schema\Sequence; | ||
16 | use Doctrine\DBAL\Schema\TableDiff; | ||
17 | use Doctrine\DBAL\TransactionIsolationLevel; | ||
18 | use Doctrine\DBAL\Types\Types; | ||
19 | use InvalidArgumentException; | ||
20 | |||
21 | use function array_merge; | ||
22 | use function count; | ||
23 | use function explode; | ||
24 | use function implode; | ||
25 | use function sprintf; | ||
26 | use function str_contains; | ||
27 | use function strlen; | ||
28 | use function strtoupper; | ||
29 | use function substr; | ||
30 | |||
31 | /** | ||
32 | * OraclePlatform. | ||
33 | */ | ||
34 | class OraclePlatform extends AbstractPlatform | ||
35 | { | ||
36 | public function getSubstringExpression(string $string, string $start, ?string $length = null): string | ||
37 | { | ||
38 | if ($length === null) { | ||
39 | return sprintf('SUBSTR(%s, %s)', $string, $start); | ||
40 | } | ||
41 | |||
42 | return sprintf('SUBSTR(%s, %s, %s)', $string, $start, $length); | ||
43 | } | ||
44 | |||
45 | public function getLocateExpression(string $string, string $substring, ?string $start = null): string | ||
46 | { | ||
47 | if ($start === null) { | ||
48 | return sprintf('INSTR(%s, %s)', $string, $substring); | ||
49 | } | ||
50 | |||
51 | return sprintf('INSTR(%s, %s, %s)', $string, $substring, $start); | ||
52 | } | ||
53 | |||
54 | protected function getDateArithmeticIntervalExpression( | ||
55 | string $date, | ||
56 | string $operator, | ||
57 | string $interval, | ||
58 | DateIntervalUnit $unit, | ||
59 | ): string { | ||
60 | switch ($unit) { | ||
61 | case DateIntervalUnit::MONTH: | ||
62 | case DateIntervalUnit::QUARTER: | ||
63 | case DateIntervalUnit::YEAR: | ||
64 | switch ($unit) { | ||
65 | case DateIntervalUnit::QUARTER: | ||
66 | $interval = $this->multiplyInterval($interval, 3); | ||
67 | break; | ||
68 | |||
69 | case DateIntervalUnit::YEAR: | ||
70 | $interval = $this->multiplyInterval($interval, 12); | ||
71 | break; | ||
72 | } | ||
73 | |||
74 | return 'ADD_MONTHS(' . $date . ', ' . $operator . $interval . ')'; | ||
75 | |||
76 | default: | ||
77 | $calculationClause = ''; | ||
78 | |||
79 | switch ($unit) { | ||
80 | case DateIntervalUnit::SECOND: | ||
81 | $calculationClause = '/24/60/60'; | ||
82 | break; | ||
83 | |||
84 | case DateIntervalUnit::MINUTE: | ||
85 | $calculationClause = '/24/60'; | ||
86 | break; | ||
87 | |||
88 | case DateIntervalUnit::HOUR: | ||
89 | $calculationClause = '/24'; | ||
90 | break; | ||
91 | |||
92 | case DateIntervalUnit::WEEK: | ||
93 | $calculationClause = '*7'; | ||
94 | break; | ||
95 | } | ||
96 | |||
97 | return '(' . $date . $operator . $interval . $calculationClause . ')'; | ||
98 | } | ||
99 | } | ||
100 | |||
101 | public function getDateDiffExpression(string $date1, string $date2): string | ||
102 | { | ||
103 | return sprintf('TRUNC(%s) - TRUNC(%s)', $date1, $date2); | ||
104 | } | ||
105 | |||
106 | public function getBitAndComparisonExpression(string $value1, string $value2): string | ||
107 | { | ||
108 | return 'BITAND(' . $value1 . ', ' . $value2 . ')'; | ||
109 | } | ||
110 | |||
111 | public function getCurrentDatabaseExpression(): string | ||
112 | { | ||
113 | return "SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA')"; | ||
114 | } | ||
115 | |||
116 | public function getBitOrComparisonExpression(string $value1, string $value2): string | ||
117 | { | ||
118 | return '(' . $value1 . '-' . | ||
119 | $this->getBitAndComparisonExpression($value1, $value2) | ||
120 | . '+' . $value2 . ')'; | ||
121 | } | ||
122 | |||
123 | public function getCreatePrimaryKeySQL(Index $index, string $table): string | ||
124 | { | ||
125 | return 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $index->getQuotedName($this) | ||
126 | . ' PRIMARY KEY (' . implode(', ', $index->getQuotedColumns($this)) . ')'; | ||
127 | } | ||
128 | |||
129 | /** | ||
130 | * {@inheritDoc} | ||
131 | * | ||
132 | * Need to specifiy minvalue, since start with is hidden in the system and MINVALUE <= START WITH. | ||
133 | * Therefore we can use MINVALUE to be able to get a hint what START WITH was for later introspection | ||
134 | * in {@see listSequences()} | ||
135 | */ | ||
136 | public function getCreateSequenceSQL(Sequence $sequence): string | ||
137 | { | ||
138 | return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . | ||
139 | ' START WITH ' . $sequence->getInitialValue() . | ||
140 | ' MINVALUE ' . $sequence->getInitialValue() . | ||
141 | ' INCREMENT BY ' . $sequence->getAllocationSize() . | ||
142 | $this->getSequenceCacheSQL($sequence); | ||
143 | } | ||
144 | |||
145 | public function getAlterSequenceSQL(Sequence $sequence): string | ||
146 | { | ||
147 | return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . | ||
148 | ' INCREMENT BY ' . $sequence->getAllocationSize() | ||
149 | . $this->getSequenceCacheSQL($sequence); | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * Cache definition for sequences | ||
154 | */ | ||
155 | private function getSequenceCacheSQL(Sequence $sequence): string | ||
156 | { | ||
157 | if ($sequence->getCache() === 0) { | ||
158 | return ' NOCACHE'; | ||
159 | } | ||
160 | |||
161 | if ($sequence->getCache() === 1) { | ||
162 | return ' NOCACHE'; | ||
163 | } | ||
164 | |||
165 | if ($sequence->getCache() > 1) { | ||
166 | return ' CACHE ' . $sequence->getCache(); | ||
167 | } | ||
168 | |||
169 | return ''; | ||
170 | } | ||
171 | |||
172 | public function getSequenceNextValSQL(string $sequence): string | ||
173 | { | ||
174 | return 'SELECT ' . $sequence . '.nextval FROM DUAL'; | ||
175 | } | ||
176 | |||
177 | public function getSetTransactionIsolationSQL(TransactionIsolationLevel $level): string | ||
178 | { | ||
179 | return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); | ||
180 | } | ||
181 | |||
182 | protected function _getTransactionIsolationLevelSQL(TransactionIsolationLevel $level): string | ||
183 | { | ||
184 | return match ($level) { | ||
185 | TransactionIsolationLevel::READ_UNCOMMITTED => 'READ UNCOMMITTED', | ||
186 | TransactionIsolationLevel::READ_COMMITTED => 'READ COMMITTED', | ||
187 | TransactionIsolationLevel::REPEATABLE_READ, | ||
188 | TransactionIsolationLevel::SERIALIZABLE => 'SERIALIZABLE', | ||
189 | }; | ||
190 | } | ||
191 | |||
192 | /** | ||
193 | * {@inheritDoc} | ||
194 | */ | ||
195 | public function getBooleanTypeDeclarationSQL(array $column): string | ||
196 | { | ||
197 | return 'NUMBER(1)'; | ||
198 | } | ||
199 | |||
200 | /** | ||
201 | * {@inheritDoc} | ||
202 | */ | ||
203 | public function getIntegerTypeDeclarationSQL(array $column): string | ||
204 | { | ||
205 | return 'NUMBER(10)'; | ||
206 | } | ||
207 | |||
208 | /** | ||
209 | * {@inheritDoc} | ||
210 | */ | ||
211 | public function getBigIntTypeDeclarationSQL(array $column): string | ||
212 | { | ||
213 | return 'NUMBER(20)'; | ||
214 | } | ||
215 | |||
216 | /** | ||
217 | * {@inheritDoc} | ||
218 | */ | ||
219 | public function getSmallIntTypeDeclarationSQL(array $column): string | ||
220 | { | ||
221 | return 'NUMBER(5)'; | ||
222 | } | ||
223 | |||
224 | /** | ||
225 | * {@inheritDoc} | ||
226 | */ | ||
227 | public function getDateTimeTypeDeclarationSQL(array $column): string | ||
228 | { | ||
229 | return 'TIMESTAMP(0)'; | ||
230 | } | ||
231 | |||
232 | /** | ||
233 | * {@inheritDoc} | ||
234 | */ | ||
235 | public function getDateTimeTzTypeDeclarationSQL(array $column): string | ||
236 | { | ||
237 | return 'TIMESTAMP(0) WITH TIME ZONE'; | ||
238 | } | ||
239 | |||
240 | /** | ||
241 | * {@inheritDoc} | ||
242 | */ | ||
243 | public function getDateTypeDeclarationSQL(array $column): string | ||
244 | { | ||
245 | return 'DATE'; | ||
246 | } | ||
247 | |||
248 | /** | ||
249 | * {@inheritDoc} | ||
250 | */ | ||
251 | public function getTimeTypeDeclarationSQL(array $column): string | ||
252 | { | ||
253 | return 'DATE'; | ||
254 | } | ||
255 | |||
256 | /** | ||
257 | * {@inheritDoc} | ||
258 | */ | ||
259 | protected function _getCommonIntegerTypeDeclarationSQL(array $column): string | ||
260 | { | ||
261 | return ''; | ||
262 | } | ||
263 | |||
264 | protected function getVarcharTypeDeclarationSQLSnippet(?int $length): string | ||
265 | { | ||
266 | if ($length === null) { | ||
267 | throw ColumnLengthRequired::new($this, 'VARCHAR2'); | ||
268 | } | ||
269 | |||
270 | return sprintf('VARCHAR2(%d)', $length); | ||
271 | } | ||
272 | |||
273 | protected function getBinaryTypeDeclarationSQLSnippet(?int $length): string | ||
274 | { | ||
275 | if ($length === null) { | ||
276 | throw ColumnLengthRequired::new($this, 'RAW'); | ||
277 | } | ||
278 | |||
279 | return sprintf('RAW(%d)', $length); | ||
280 | } | ||
281 | |||
282 | protected function getVarbinaryTypeDeclarationSQLSnippet(?int $length): string | ||
283 | { | ||
284 | return $this->getBinaryTypeDeclarationSQLSnippet($length); | ||
285 | } | ||
286 | |||
287 | /** | ||
288 | * {@inheritDoc} | ||
289 | */ | ||
290 | public function getClobTypeDeclarationSQL(array $column): string | ||
291 | { | ||
292 | return 'CLOB'; | ||
293 | } | ||
294 | |||
295 | /** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ | ||
296 | public function getListDatabasesSQL(): string | ||
297 | { | ||
298 | return 'SELECT username FROM all_users'; | ||
299 | } | ||
300 | |||
301 | /** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ | ||
302 | public function getListSequencesSQL(string $database): string | ||
303 | { | ||
304 | return 'SELECT SEQUENCE_NAME, MIN_VALUE, INCREMENT_BY FROM SYS.ALL_SEQUENCES WHERE SEQUENCE_OWNER = ' | ||
305 | . $this->quoteStringLiteral( | ||
306 | $this->normalizeIdentifier($database)->getName(), | ||
307 | ); | ||
308 | } | ||
309 | |||
310 | /** | ||
311 | * {@inheritDoc} | ||
312 | */ | ||
313 | protected function _getCreateTableSQL(string $name, array $columns, array $options = []): array | ||
314 | { | ||
315 | $indexes = $options['indexes'] ?? []; | ||
316 | $options['indexes'] = []; | ||
317 | $sql = parent::_getCreateTableSQL($name, $columns, $options); | ||
318 | |||
319 | foreach ($columns as $column) { | ||
320 | if (isset($column['sequence'])) { | ||
321 | $sql[] = $this->getCreateSequenceSQL($column['sequence']); | ||
322 | } | ||
323 | |||
324 | if ( | ||
325 | empty($column['autoincrement']) | ||
326 | ) { | ||
327 | continue; | ||
328 | } | ||
329 | |||
330 | $sql = array_merge($sql, $this->getCreateAutoincrementSql($column['name'], $name)); | ||
331 | } | ||
332 | |||
333 | foreach ($indexes as $index) { | ||
334 | $sql[] = $this->getCreateIndexSQL($index, $name); | ||
335 | } | ||
336 | |||
337 | return $sql; | ||
338 | } | ||
339 | |||
340 | /** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ | ||
341 | public function getListViewsSQL(string $database): string | ||
342 | { | ||
343 | return 'SELECT view_name, text FROM sys.user_views'; | ||
344 | } | ||
345 | |||
346 | /** @return array<int, string> */ | ||
347 | protected function getCreateAutoincrementSql(string $name, string $table, int $start = 1): array | ||
348 | { | ||
349 | $tableIdentifier = $this->normalizeIdentifier($table); | ||
350 | $quotedTableName = $tableIdentifier->getQuotedName($this); | ||
351 | $unquotedTableName = $tableIdentifier->getName(); | ||
352 | |||
353 | $nameIdentifier = $this->normalizeIdentifier($name); | ||
354 | $quotedName = $nameIdentifier->getQuotedName($this); | ||
355 | $unquotedName = $nameIdentifier->getName(); | ||
356 | |||
357 | $sql = []; | ||
358 | |||
359 | $autoincrementIdentifierName = $this->getAutoincrementIdentifierName($tableIdentifier); | ||
360 | |||
361 | $idx = new Index($autoincrementIdentifierName, [$quotedName], true, true); | ||
362 | |||
363 | $sql[] = "DECLARE | ||
364 | constraints_Count NUMBER; | ||
365 | BEGIN | ||
366 | SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count | ||
367 | FROM USER_CONSTRAINTS | ||
368 | WHERE TABLE_NAME = '" . $unquotedTableName . "' | ||
369 | AND CONSTRAINT_TYPE = 'P'; | ||
370 | IF constraints_Count = 0 OR constraints_Count = '' THEN | ||
371 | EXECUTE IMMEDIATE '" . $this->getCreateIndexSQL($idx, $quotedTableName) . "'; | ||
372 | END IF; | ||
373 | END;"; | ||
374 | |||
375 | $sequenceName = $this->getIdentitySequenceName( | ||
376 | $tableIdentifier->isQuoted() ? $quotedTableName : $unquotedTableName, | ||
377 | ); | ||
378 | $sequence = new Sequence($sequenceName, $start); | ||
379 | $sql[] = $this->getCreateSequenceSQL($sequence); | ||
380 | |||
381 | $sql[] = 'CREATE TRIGGER ' . $autoincrementIdentifierName . ' | ||
382 | BEFORE INSERT | ||
383 | ON ' . $quotedTableName . ' | ||
384 | FOR EACH ROW | ||
385 | DECLARE | ||
386 | last_Sequence NUMBER; | ||
387 | last_InsertID NUMBER; | ||
388 | BEGIN | ||
389 | IF (:NEW.' . $quotedName . ' IS NULL OR :NEW.' . $quotedName . ' = 0) THEN | ||
390 | SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL; | ||
391 | ELSE | ||
392 | SELECT NVL(Last_Number, 0) INTO last_Sequence | ||
393 | FROM User_Sequences | ||
394 | WHERE Sequence_Name = \'' . $sequence->getName() . '\'; | ||
395 | SELECT :NEW.' . $quotedName . ' INTO last_InsertID FROM DUAL; | ||
396 | WHILE (last_InsertID > last_Sequence) LOOP | ||
397 | SELECT ' . $sequenceName . '.NEXTVAL INTO last_Sequence FROM DUAL; | ||
398 | END LOOP; | ||
399 | SELECT ' . $sequenceName . '.NEXTVAL INTO last_Sequence FROM DUAL; | ||
400 | END IF; | ||
401 | END;'; | ||
402 | |||
403 | return $sql; | ||
404 | } | ||
405 | |||
406 | /** | ||
407 | * @internal The method should be only used from within the OracleSchemaManager class hierarchy. | ||
408 | * | ||
409 | * Returns the SQL statements to drop the autoincrement for the given table name. | ||
410 | * | ||
411 | * @param string $table The table name to drop the autoincrement for. | ||
412 | * | ||
413 | * @return string[] | ||
414 | */ | ||
415 | public function getDropAutoincrementSql(string $table): array | ||
416 | { | ||
417 | $table = $this->normalizeIdentifier($table); | ||
418 | $autoincrementIdentifierName = $this->getAutoincrementIdentifierName($table); | ||
419 | $identitySequenceName = $this->getIdentitySequenceName( | ||
420 | $table->isQuoted() ? $table->getQuotedName($this) : $table->getName(), | ||
421 | ); | ||
422 | |||
423 | return [ | ||
424 | 'DROP TRIGGER ' . $autoincrementIdentifierName, | ||
425 | $this->getDropSequenceSQL($identitySequenceName), | ||
426 | $this->getDropConstraintSQL($autoincrementIdentifierName, $table->getQuotedName($this)), | ||
427 | ]; | ||
428 | } | ||
429 | |||
430 | /** | ||
431 | * Normalizes the given identifier. | ||
432 | * | ||
433 | * Uppercases the given identifier if it is not quoted by intention | ||
434 | * to reflect Oracle's internal auto uppercasing strategy of unquoted identifiers. | ||
435 | * | ||
436 | * @param string $name The identifier to normalize. | ||
437 | */ | ||
438 | private function normalizeIdentifier(string $name): Identifier | ||
439 | { | ||
440 | $identifier = new Identifier($name); | ||
441 | |||
442 | return $identifier->isQuoted() ? $identifier : new Identifier(strtoupper($name)); | ||
443 | } | ||
444 | |||
445 | /** | ||
446 | * Adds suffix to identifier, | ||
447 | * | ||
448 | * if the new string exceeds max identifier length, | ||
449 | * keeps $suffix, cuts from $identifier as much as the part exceeding. | ||
450 | */ | ||
451 | private function addSuffix(string $identifier, string $suffix): string | ||
452 | { | ||
453 | $maxPossibleLengthWithoutSuffix = $this->getMaxIdentifierLength() - strlen($suffix); | ||
454 | if (strlen($identifier) > $maxPossibleLengthWithoutSuffix) { | ||
455 | $identifier = substr($identifier, 0, $maxPossibleLengthWithoutSuffix); | ||
456 | } | ||
457 | |||
458 | return $identifier . $suffix; | ||
459 | } | ||
460 | |||
461 | /** | ||
462 | * Returns the autoincrement primary key identifier name for the given table identifier. | ||
463 | * | ||
464 | * Quotes the autoincrement primary key identifier name | ||
465 | * if the given table name is quoted by intention. | ||
466 | */ | ||
467 | private function getAutoincrementIdentifierName(Identifier $table): string | ||
468 | { | ||
469 | $identifierName = $this->addSuffix($table->getName(), '_AI_PK'); | ||
470 | |||
471 | return $table->isQuoted() | ||
472 | ? $this->quoteSingleIdentifier($identifierName) | ||
473 | : $identifierName; | ||
474 | } | ||
475 | |||
476 | public function getDropForeignKeySQL(string $foreignKey, string $table): string | ||
477 | { | ||
478 | return $this->getDropConstraintSQL($foreignKey, $table); | ||
479 | } | ||
480 | |||
481 | /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ | ||
482 | public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey): string | ||
483 | { | ||
484 | $referentialAction = ''; | ||
485 | |||
486 | if ($foreignKey->hasOption('onDelete')) { | ||
487 | $referentialAction = $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete')); | ||
488 | } | ||
489 | |||
490 | if ($referentialAction !== '') { | ||
491 | return ' ON DELETE ' . $referentialAction; | ||
492 | } | ||
493 | |||
494 | return ''; | ||
495 | } | ||
496 | |||
497 | /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ | ||
498 | public function getForeignKeyReferentialActionSQL(string $action): string | ||
499 | { | ||
500 | $action = strtoupper($action); | ||
501 | |||
502 | return match ($action) { | ||
503 | 'RESTRICT', | ||
504 | 'NO ACTION' => '', | ||
505 | 'CASCADE', | ||
506 | 'SET NULL' => $action, | ||
507 | default => throw new InvalidArgumentException(sprintf('Invalid foreign key action "%s".', $action)), | ||
508 | }; | ||
509 | } | ||
510 | |||
511 | public function getCreateDatabaseSQL(string $name): string | ||
512 | { | ||
513 | return 'CREATE USER ' . $name; | ||
514 | } | ||
515 | |||
516 | public function getDropDatabaseSQL(string $name): string | ||
517 | { | ||
518 | return 'DROP USER ' . $name . ' CASCADE'; | ||
519 | } | ||
520 | |||
521 | /** | ||
522 | * {@inheritDoc} | ||
523 | */ | ||
524 | public function getAlterTableSQL(TableDiff $diff): array | ||
525 | { | ||
526 | $sql = []; | ||
527 | $commentsSQL = []; | ||
528 | $columnSql = []; | ||
529 | |||
530 | $addColumnSQL = []; | ||
531 | |||
532 | $tableNameSQL = $diff->getOldTable()->getQuotedName($this); | ||
533 | |||
534 | foreach ($diff->getAddedColumns() as $column) { | ||
535 | $addColumnSQL[] = $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); | ||
536 | $comment = $column->getComment(); | ||
537 | |||
538 | if ($comment === '') { | ||
539 | continue; | ||
540 | } | ||
541 | |||
542 | $commentsSQL[] = $this->getCommentOnColumnSQL( | ||
543 | $tableNameSQL, | ||
544 | $column->getQuotedName($this), | ||
545 | $comment, | ||
546 | ); | ||
547 | } | ||
548 | |||
549 | if (count($addColumnSQL) > 0) { | ||
550 | $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ADD (' . implode(', ', $addColumnSQL) . ')'; | ||
551 | } | ||
552 | |||
553 | $modifyColumnSQL = []; | ||
554 | foreach ($diff->getModifiedColumns() as $columnDiff) { | ||
555 | $newColumn = $columnDiff->getNewColumn(); | ||
556 | $oldColumn = $columnDiff->getOldColumn(); | ||
557 | |||
558 | $newColumnProperties = $newColumn->toArray(); | ||
559 | $oldColumnProperties = $oldColumn->toArray(); | ||
560 | |||
561 | $oldSQL = $this->getColumnDeclarationSQL('', $oldColumnProperties); | ||
562 | $newSQL = $this->getColumnDeclarationSQL('', $newColumnProperties); | ||
563 | |||
564 | if ($newSQL !== $oldSQL) { | ||
565 | if (! $columnDiff->hasNotNullChanged()) { | ||
566 | unset($newColumnProperties['notnull']); | ||
567 | $newSQL = $this->getColumnDeclarationSQL('', $newColumnProperties); | ||
568 | } | ||
569 | |||
570 | $modifyColumnSQL[] = $newColumn->getQuotedName($this) . $newSQL; | ||
571 | } | ||
572 | |||
573 | if (! $columnDiff->hasCommentChanged()) { | ||
574 | continue; | ||
575 | } | ||
576 | |||
577 | $commentsSQL[] = $this->getCommentOnColumnSQL( | ||
578 | $tableNameSQL, | ||
579 | $newColumn->getQuotedName($this), | ||
580 | $newColumn->getComment(), | ||
581 | ); | ||
582 | } | ||
583 | |||
584 | if (count($modifyColumnSQL) > 0) { | ||
585 | $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' MODIFY (' . implode(', ', $modifyColumnSQL) . ')'; | ||
586 | } | ||
587 | |||
588 | foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { | ||
589 | $oldColumnName = new Identifier($oldColumnName); | ||
590 | |||
591 | $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) | ||
592 | . ' TO ' . $column->getQuotedName($this); | ||
593 | } | ||
594 | |||
595 | $dropColumnSQL = []; | ||
596 | foreach ($diff->getDroppedColumns() as $column) { | ||
597 | $dropColumnSQL[] = $column->getQuotedName($this); | ||
598 | } | ||
599 | |||
600 | if (count($dropColumnSQL) > 0) { | ||
601 | $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' DROP (' . implode(', ', $dropColumnSQL) . ')'; | ||
602 | } | ||
603 | |||
604 | return array_merge( | ||
605 | $this->getPreAlterTableIndexForeignKeySQL($diff), | ||
606 | $sql, | ||
607 | $commentsSQL, | ||
608 | $this->getPostAlterTableIndexForeignKeySQL($diff), | ||
609 | $columnSql, | ||
610 | ); | ||
611 | } | ||
612 | |||
613 | /** | ||
614 | * {@inheritDoc} | ||
615 | * | ||
616 | * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. | ||
617 | */ | ||
618 | public function getColumnDeclarationSQL(string $name, array $column): string | ||
619 | { | ||
620 | if (isset($column['columnDefinition'])) { | ||
621 | $declaration = $column['columnDefinition']; | ||
622 | } else { | ||
623 | $default = $this->getDefaultValueDeclarationSQL($column); | ||
624 | |||
625 | $notnull = ''; | ||
626 | |||
627 | if (isset($column['notnull'])) { | ||
628 | $notnull = $column['notnull'] ? ' NOT NULL' : ' NULL'; | ||
629 | } | ||
630 | |||
631 | $typeDecl = $column['type']->getSQLDeclaration($column, $this); | ||
632 | $declaration = $typeDecl . $default . $notnull; | ||
633 | } | ||
634 | |||
635 | return $name . ' ' . $declaration; | ||
636 | } | ||
637 | |||
638 | /** | ||
639 | * {@inheritDoc} | ||
640 | */ | ||
641 | protected function getRenameIndexSQL(string $oldIndexName, Index $index, string $tableName): array | ||
642 | { | ||
643 | if (str_contains($tableName, '.')) { | ||
644 | [$schema] = explode('.', $tableName); | ||
645 | $oldIndexName = $schema . '.' . $oldIndexName; | ||
646 | } | ||
647 | |||
648 | return ['ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)]; | ||
649 | } | ||
650 | |||
651 | protected function getIdentitySequenceName(string $tableName): string | ||
652 | { | ||
653 | $table = new Identifier($tableName); | ||
654 | |||
655 | // No usage of column name to preserve BC compatibility with <2.5 | ||
656 | $identitySequenceName = $this->addSuffix($table->getName(), '_SEQ'); | ||
657 | |||
658 | if ($table->isQuoted()) { | ||
659 | $identitySequenceName = '"' . $identitySequenceName . '"'; | ||
660 | } | ||
661 | |||
662 | $identitySequenceIdentifier = $this->normalizeIdentifier($identitySequenceName); | ||
663 | |||
664 | return $identitySequenceIdentifier->getQuotedName($this); | ||
665 | } | ||
666 | |||
667 | /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ | ||
668 | public function supportsCommentOnStatement(): bool | ||
669 | { | ||
670 | return true; | ||
671 | } | ||
672 | |||
673 | protected function doModifyLimitQuery(string $query, ?int $limit, int $offset): string | ||
674 | { | ||
675 | if ($offset > 0) { | ||
676 | $query .= sprintf(' OFFSET %d ROWS', $offset); | ||
677 | } | ||
678 | |||
679 | if ($limit !== null) { | ||
680 | $query .= sprintf(' FETCH NEXT %d ROWS ONLY', $limit); | ||
681 | } | ||
682 | |||
683 | return $query; | ||
684 | } | ||
685 | |||
686 | public function getCreateTemporaryTableSnippetSQL(): string | ||
687 | { | ||
688 | return 'CREATE GLOBAL TEMPORARY TABLE'; | ||
689 | } | ||
690 | |||
691 | public function getDateTimeTzFormatString(): string | ||
692 | { | ||
693 | return 'Y-m-d H:i:sP'; | ||
694 | } | ||
695 | |||
696 | public function getDateFormatString(): string | ||
697 | { | ||
698 | return 'Y-m-d 00:00:00'; | ||
699 | } | ||
700 | |||
701 | public function getTimeFormatString(): string | ||
702 | { | ||
703 | return '1900-01-01 H:i:s'; | ||
704 | } | ||
705 | |||
706 | public function getMaxIdentifierLength(): int | ||
707 | { | ||
708 | return 128; | ||
709 | } | ||
710 | |||
711 | public function supportsSequences(): bool | ||
712 | { | ||
713 | return true; | ||
714 | } | ||
715 | |||
716 | public function supportsReleaseSavepoints(): bool | ||
717 | { | ||
718 | return false; | ||
719 | } | ||
720 | |||
721 | public function getTruncateTableSQL(string $tableName, bool $cascade = false): string | ||
722 | { | ||
723 | $tableIdentifier = new Identifier($tableName); | ||
724 | |||
725 | return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this); | ||
726 | } | ||
727 | |||
728 | public function getDummySelectSQL(string $expression = '1'): string | ||
729 | { | ||
730 | return sprintf('SELECT %s FROM DUAL', $expression); | ||
731 | } | ||
732 | |||
733 | protected function initializeDoctrineTypeMappings(): void | ||
734 | { | ||
735 | $this->doctrineTypeMapping = [ | ||
736 | 'binary_double' => Types::FLOAT, | ||
737 | 'binary_float' => Types::FLOAT, | ||
738 | 'binary_integer' => Types::BOOLEAN, | ||
739 | 'blob' => Types::BLOB, | ||
740 | 'char' => Types::STRING, | ||
741 | 'clob' => Types::TEXT, | ||
742 | 'date' => Types::DATE_MUTABLE, | ||
743 | 'float' => Types::FLOAT, | ||
744 | 'integer' => Types::INTEGER, | ||
745 | 'long' => Types::STRING, | ||
746 | 'long raw' => Types::BLOB, | ||
747 | 'nchar' => Types::STRING, | ||
748 | 'nclob' => Types::TEXT, | ||
749 | 'number' => Types::INTEGER, | ||
750 | 'nvarchar2' => Types::STRING, | ||
751 | 'pls_integer' => Types::BOOLEAN, | ||
752 | 'raw' => Types::BINARY, | ||
753 | 'rowid' => Types::STRING, | ||
754 | 'timestamp' => Types::DATETIME_MUTABLE, | ||
755 | 'timestamptz' => Types::DATETIMETZ_MUTABLE, | ||
756 | 'urowid' => Types::STRING, | ||
757 | 'varchar' => Types::STRING, | ||
758 | 'varchar2' => Types::STRING, | ||
759 | ]; | ||
760 | } | ||
761 | |||
762 | public function releaseSavePoint(string $savepoint): string | ||
763 | { | ||
764 | return ''; | ||
765 | } | ||
766 | |||
767 | protected function createReservedKeywordsList(): KeywordList | ||
768 | { | ||
769 | return new OracleKeywords(); | ||
770 | } | ||
771 | |||
772 | /** | ||
773 | * {@inheritDoc} | ||
774 | */ | ||
775 | public function getBlobTypeDeclarationSQL(array $column): string | ||
776 | { | ||
777 | return 'BLOB'; | ||
778 | } | ||
779 | |||
780 | public function createSchemaManager(Connection $connection): OracleSchemaManager | ||
781 | { | ||
782 | return new OracleSchemaManager($connection, $this); | ||
783 | } | ||
784 | } | ||