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 | } | ||
