diff options
Diffstat (limited to 'vendor/doctrine/dbal/src/Schema/OracleSchemaManager.php')
| -rw-r--r-- | vendor/doctrine/dbal/src/Schema/OracleSchemaManager.php | 475 |
1 files changed, 475 insertions, 0 deletions
diff --git a/vendor/doctrine/dbal/src/Schema/OracleSchemaManager.php b/vendor/doctrine/dbal/src/Schema/OracleSchemaManager.php new file mode 100644 index 0000000..f973eaa --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/OracleSchemaManager.php | |||
| @@ -0,0 +1,475 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Exception; | ||
| 8 | use Doctrine\DBAL\Exception\DatabaseObjectNotFoundException; | ||
| 9 | use Doctrine\DBAL\Platforms\OraclePlatform; | ||
| 10 | use Doctrine\DBAL\Result; | ||
| 11 | use Doctrine\DBAL\Types\Type; | ||
| 12 | |||
| 13 | use function array_change_key_case; | ||
| 14 | use function array_key_exists; | ||
| 15 | use function array_values; | ||
| 16 | use function assert; | ||
| 17 | use function implode; | ||
| 18 | use function is_string; | ||
| 19 | use function preg_match; | ||
| 20 | use function str_contains; | ||
| 21 | use function str_replace; | ||
| 22 | use function str_starts_with; | ||
| 23 | use function strtolower; | ||
| 24 | use function strtoupper; | ||
| 25 | use function trim; | ||
| 26 | |||
| 27 | use const CASE_LOWER; | ||
| 28 | |||
| 29 | /** | ||
| 30 | * Oracle Schema Manager. | ||
| 31 | * | ||
| 32 | * @extends AbstractSchemaManager<OraclePlatform> | ||
| 33 | */ | ||
| 34 | class OracleSchemaManager extends AbstractSchemaManager | ||
| 35 | { | ||
| 36 | /** | ||
| 37 | * {@inheritDoc} | ||
| 38 | */ | ||
| 39 | protected function _getPortableViewDefinition(array $view): View | ||
| 40 | { | ||
| 41 | $view = array_change_key_case($view, CASE_LOWER); | ||
| 42 | |||
| 43 | return new View($this->getQuotedIdentifierName($view['view_name']), $view['text']); | ||
| 44 | } | ||
| 45 | |||
| 46 | /** | ||
| 47 | * {@inheritDoc} | ||
| 48 | */ | ||
| 49 | protected function _getPortableTableDefinition(array $table): string | ||
| 50 | { | ||
| 51 | $table = array_change_key_case($table, CASE_LOWER); | ||
| 52 | |||
| 53 | return $this->getQuotedIdentifierName($table['table_name']); | ||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * {@inheritDoc} | ||
| 58 | * | ||
| 59 | * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html | ||
| 60 | */ | ||
| 61 | protected function _getPortableTableIndexesList(array $tableIndexes, string $tableName): array | ||
| 62 | { | ||
| 63 | $indexBuffer = []; | ||
| 64 | foreach ($tableIndexes as $tableIndex) { | ||
| 65 | $tableIndex = array_change_key_case($tableIndex, CASE_LOWER); | ||
| 66 | |||
| 67 | $keyName = strtolower($tableIndex['name']); | ||
| 68 | $buffer = []; | ||
| 69 | |||
| 70 | if ($tableIndex['is_primary'] === 'P') { | ||
| 71 | $keyName = 'primary'; | ||
| 72 | $buffer['primary'] = true; | ||
| 73 | $buffer['non_unique'] = false; | ||
| 74 | } else { | ||
| 75 | $buffer['primary'] = false; | ||
| 76 | $buffer['non_unique'] = ! $tableIndex['is_unique']; | ||
| 77 | } | ||
| 78 | |||
| 79 | $buffer['key_name'] = $keyName; | ||
| 80 | $buffer['column_name'] = $this->getQuotedIdentifierName($tableIndex['column_name']); | ||
| 81 | $indexBuffer[] = $buffer; | ||
| 82 | } | ||
| 83 | |||
| 84 | return parent::_getPortableTableIndexesList($indexBuffer, $tableName); | ||
| 85 | } | ||
| 86 | |||
| 87 | /** | ||
| 88 | * {@inheritDoc} | ||
| 89 | */ | ||
| 90 | protected function _getPortableTableColumnDefinition(array $tableColumn): Column | ||
| 91 | { | ||
| 92 | $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); | ||
| 93 | |||
| 94 | $dbType = strtolower($tableColumn['data_type']); | ||
| 95 | if (str_starts_with($dbType, 'timestamp(')) { | ||
| 96 | if (str_contains($dbType, 'with time zone')) { | ||
| 97 | $dbType = 'timestamptz'; | ||
| 98 | } else { | ||
| 99 | $dbType = 'timestamp'; | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | $length = $precision = null; | ||
| 104 | $scale = 0; | ||
| 105 | $fixed = false; | ||
| 106 | |||
| 107 | if (! isset($tableColumn['column_name'])) { | ||
| 108 | $tableColumn['column_name'] = ''; | ||
| 109 | } | ||
| 110 | |||
| 111 | assert(array_key_exists('data_default', $tableColumn)); | ||
| 112 | |||
| 113 | // Default values returned from database sometimes have trailing spaces. | ||
| 114 | if (is_string($tableColumn['data_default'])) { | ||
| 115 | $tableColumn['data_default'] = trim($tableColumn['data_default']); | ||
| 116 | } | ||
| 117 | |||
| 118 | if ($tableColumn['data_default'] === '' || $tableColumn['data_default'] === 'NULL') { | ||
| 119 | $tableColumn['data_default'] = null; | ||
| 120 | } | ||
| 121 | |||
| 122 | if ($tableColumn['data_default'] !== null) { | ||
| 123 | // Default values returned from database are represented as literal expressions | ||
| 124 | if (preg_match('/^\'(.*)\'$/s', $tableColumn['data_default'], $matches) === 1) { | ||
| 125 | $tableColumn['data_default'] = str_replace("''", "'", $matches[1]); | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | if ($tableColumn['data_precision'] !== null) { | ||
| 130 | $precision = (int) $tableColumn['data_precision']; | ||
| 131 | } | ||
| 132 | |||
| 133 | if ($tableColumn['data_scale'] !== null) { | ||
| 134 | $scale = (int) $tableColumn['data_scale']; | ||
| 135 | } | ||
| 136 | |||
| 137 | $type = $this->platform->getDoctrineTypeMapping($dbType); | ||
| 138 | |||
| 139 | switch ($dbType) { | ||
| 140 | case 'number': | ||
| 141 | if ($precision === 20 && $scale === 0) { | ||
| 142 | $type = 'bigint'; | ||
| 143 | } elseif ($precision === 5 && $scale === 0) { | ||
| 144 | $type = 'smallint'; | ||
| 145 | } elseif ($precision === 1 && $scale === 0) { | ||
| 146 | $type = 'boolean'; | ||
| 147 | } elseif ($scale > 0) { | ||
| 148 | $type = 'decimal'; | ||
| 149 | } | ||
| 150 | |||
| 151 | break; | ||
| 152 | |||
| 153 | case 'varchar': | ||
| 154 | case 'varchar2': | ||
| 155 | case 'nvarchar2': | ||
| 156 | $length = (int) $tableColumn['char_length']; | ||
| 157 | break; | ||
| 158 | |||
| 159 | case 'raw': | ||
| 160 | $length = (int) $tableColumn['data_length']; | ||
| 161 | $fixed = true; | ||
| 162 | break; | ||
| 163 | |||
| 164 | case 'char': | ||
| 165 | case 'nchar': | ||
| 166 | $length = (int) $tableColumn['char_length']; | ||
| 167 | $fixed = true; | ||
| 168 | break; | ||
| 169 | } | ||
| 170 | |||
| 171 | $options = [ | ||
| 172 | 'notnull' => $tableColumn['nullable'] === 'N', | ||
| 173 | 'fixed' => $fixed, | ||
| 174 | 'default' => $tableColumn['data_default'], | ||
| 175 | 'length' => $length, | ||
| 176 | 'precision' => $precision, | ||
| 177 | 'scale' => $scale, | ||
| 178 | ]; | ||
| 179 | |||
| 180 | if (isset($tableColumn['comments'])) { | ||
| 181 | $options['comment'] = $tableColumn['comments']; | ||
| 182 | } | ||
| 183 | |||
| 184 | return new Column($this->getQuotedIdentifierName($tableColumn['column_name']), Type::getType($type), $options); | ||
| 185 | } | ||
| 186 | |||
| 187 | /** | ||
| 188 | * {@inheritDoc} | ||
| 189 | */ | ||
| 190 | protected function _getPortableTableForeignKeysList(array $tableForeignKeys): array | ||
| 191 | { | ||
| 192 | $list = []; | ||
| 193 | foreach ($tableForeignKeys as $value) { | ||
| 194 | $value = array_change_key_case($value, CASE_LOWER); | ||
| 195 | if (! isset($list[$value['constraint_name']])) { | ||
| 196 | if ($value['delete_rule'] === 'NO ACTION') { | ||
| 197 | $value['delete_rule'] = null; | ||
| 198 | } | ||
| 199 | |||
| 200 | $list[$value['constraint_name']] = [ | ||
| 201 | 'name' => $this->getQuotedIdentifierName($value['constraint_name']), | ||
| 202 | 'local' => [], | ||
| 203 | 'foreign' => [], | ||
| 204 | 'foreignTable' => $value['references_table'], | ||
| 205 | 'onDelete' => $value['delete_rule'], | ||
| 206 | ]; | ||
| 207 | } | ||
| 208 | |||
| 209 | $localColumn = $this->getQuotedIdentifierName($value['local_column']); | ||
| 210 | $foreignColumn = $this->getQuotedIdentifierName($value['foreign_column']); | ||
| 211 | |||
| 212 | $list[$value['constraint_name']]['local'][$value['position']] = $localColumn; | ||
| 213 | $list[$value['constraint_name']]['foreign'][$value['position']] = $foreignColumn; | ||
| 214 | } | ||
| 215 | |||
| 216 | return parent::_getPortableTableForeignKeysList($list); | ||
| 217 | } | ||
| 218 | |||
| 219 | /** | ||
| 220 | * {@inheritDoc} | ||
| 221 | */ | ||
| 222 | protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey): ForeignKeyConstraint | ||
| 223 | { | ||
| 224 | return new ForeignKeyConstraint( | ||
| 225 | array_values($tableForeignKey['local']), | ||
| 226 | $this->getQuotedIdentifierName($tableForeignKey['foreignTable']), | ||
| 227 | array_values($tableForeignKey['foreign']), | ||
| 228 | $this->getQuotedIdentifierName($tableForeignKey['name']), | ||
| 229 | ['onDelete' => $tableForeignKey['onDelete']], | ||
| 230 | ); | ||
| 231 | } | ||
| 232 | |||
| 233 | /** | ||
| 234 | * {@inheritDoc} | ||
| 235 | */ | ||
| 236 | protected function _getPortableSequenceDefinition(array $sequence): Sequence | ||
| 237 | { | ||
| 238 | $sequence = array_change_key_case($sequence, CASE_LOWER); | ||
| 239 | |||
| 240 | return new Sequence( | ||
| 241 | $this->getQuotedIdentifierName($sequence['sequence_name']), | ||
| 242 | (int) $sequence['increment_by'], | ||
| 243 | (int) $sequence['min_value'], | ||
| 244 | ); | ||
| 245 | } | ||
| 246 | |||
| 247 | /** | ||
| 248 | * {@inheritDoc} | ||
| 249 | */ | ||
| 250 | protected function _getPortableDatabaseDefinition(array $database): string | ||
| 251 | { | ||
| 252 | $database = array_change_key_case($database, CASE_LOWER); | ||
| 253 | |||
| 254 | return $database['username']; | ||
| 255 | } | ||
| 256 | |||
| 257 | public function createDatabase(string $database): void | ||
| 258 | { | ||
| 259 | $statement = $this->platform->getCreateDatabaseSQL($database); | ||
| 260 | |||
| 261 | $params = $this->connection->getParams(); | ||
| 262 | |||
| 263 | if (isset($params['password'])) { | ||
| 264 | $statement .= ' IDENTIFIED BY ' . $params['password']; | ||
| 265 | } | ||
| 266 | |||
| 267 | $this->connection->executeStatement($statement); | ||
| 268 | |||
| 269 | $statement = 'GRANT DBA TO ' . $database; | ||
| 270 | $this->connection->executeStatement($statement); | ||
| 271 | } | ||
| 272 | |||
| 273 | /** @throws Exception */ | ||
| 274 | protected function dropAutoincrement(string $table): bool | ||
| 275 | { | ||
| 276 | $sql = $this->platform->getDropAutoincrementSql($table); | ||
| 277 | foreach ($sql as $query) { | ||
| 278 | $this->connection->executeStatement($query); | ||
| 279 | } | ||
| 280 | |||
| 281 | return true; | ||
| 282 | } | ||
| 283 | |||
| 284 | public function dropTable(string $name): void | ||
| 285 | { | ||
| 286 | try { | ||
| 287 | $this->dropAutoincrement($name); | ||
| 288 | } catch (DatabaseObjectNotFoundException) { | ||
| 289 | } | ||
| 290 | |||
| 291 | parent::dropTable($name); | ||
| 292 | } | ||
| 293 | |||
| 294 | /** | ||
| 295 | * Returns the quoted representation of the given identifier name. | ||
| 296 | * | ||
| 297 | * Quotes non-uppercase identifiers explicitly to preserve case | ||
| 298 | * and thus make references to the particular identifier work. | ||
| 299 | */ | ||
| 300 | private function getQuotedIdentifierName(string $identifier): string | ||
| 301 | { | ||
| 302 | if (preg_match('/[a-z]/', $identifier) === 1) { | ||
| 303 | return $this->platform->quoteIdentifier($identifier); | ||
| 304 | } | ||
| 305 | |||
| 306 | return $identifier; | ||
| 307 | } | ||
| 308 | |||
| 309 | protected function selectTableNames(string $databaseName): Result | ||
| 310 | { | ||
| 311 | $sql = <<<'SQL' | ||
| 312 | SELECT TABLE_NAME | ||
| 313 | FROM ALL_TABLES | ||
| 314 | WHERE OWNER = :OWNER | ||
| 315 | ORDER BY TABLE_NAME | ||
| 316 | SQL; | ||
| 317 | |||
| 318 | return $this->connection->executeQuery($sql, ['OWNER' => $databaseName]); | ||
| 319 | } | ||
| 320 | |||
| 321 | protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result | ||
| 322 | { | ||
| 323 | $sql = 'SELECT'; | ||
| 324 | |||
| 325 | if ($tableName === null) { | ||
| 326 | $sql .= ' C.TABLE_NAME,'; | ||
| 327 | } | ||
| 328 | |||
| 329 | $sql .= <<<'SQL' | ||
| 330 | C.COLUMN_NAME, | ||
| 331 | C.DATA_TYPE, | ||
| 332 | C.DATA_DEFAULT, | ||
| 333 | C.DATA_PRECISION, | ||
| 334 | C.DATA_SCALE, | ||
| 335 | C.CHAR_LENGTH, | ||
| 336 | C.DATA_LENGTH, | ||
| 337 | C.NULLABLE, | ||
| 338 | D.COMMENTS | ||
| 339 | FROM ALL_TAB_COLUMNS C | ||
| 340 | INNER JOIN ALL_TABLES T | ||
| 341 | ON T.OWNER = C.OWNER | ||
| 342 | AND T.TABLE_NAME = C.TABLE_NAME | ||
| 343 | LEFT JOIN ALL_COL_COMMENTS D | ||
| 344 | ON D.OWNER = C.OWNER | ||
| 345 | AND D.TABLE_NAME = C.TABLE_NAME | ||
| 346 | AND D.COLUMN_NAME = C.COLUMN_NAME | ||
| 347 | SQL; | ||
| 348 | |||
| 349 | $conditions = ['C.OWNER = :OWNER']; | ||
| 350 | $params = ['OWNER' => $databaseName]; | ||
| 351 | |||
| 352 | if ($tableName !== null) { | ||
| 353 | $conditions[] = 'C.TABLE_NAME = :TABLE_NAME'; | ||
| 354 | $params['TABLE_NAME'] = $tableName; | ||
| 355 | } | ||
| 356 | |||
| 357 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY C.COLUMN_ID'; | ||
| 358 | |||
| 359 | return $this->connection->executeQuery($sql, $params); | ||
| 360 | } | ||
| 361 | |||
| 362 | protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result | ||
| 363 | { | ||
| 364 | $sql = 'SELECT'; | ||
| 365 | |||
| 366 | if ($tableName === null) { | ||
| 367 | $sql .= ' IND_COL.TABLE_NAME,'; | ||
| 368 | } | ||
| 369 | |||
| 370 | $sql .= <<<'SQL' | ||
| 371 | IND_COL.INDEX_NAME AS NAME, | ||
| 372 | IND.INDEX_TYPE AS TYPE, | ||
| 373 | DECODE(IND.UNIQUENESS, 'NONUNIQUE', 0, 'UNIQUE', 1) AS IS_UNIQUE, | ||
| 374 | IND_COL.COLUMN_NAME, | ||
| 375 | IND_COL.COLUMN_POSITION AS COLUMN_POS, | ||
| 376 | CON.CONSTRAINT_TYPE AS IS_PRIMARY | ||
| 377 | FROM ALL_IND_COLUMNS IND_COL | ||
| 378 | LEFT JOIN ALL_INDEXES IND | ||
| 379 | ON IND.OWNER = IND_COL.INDEX_OWNER | ||
| 380 | AND IND.INDEX_NAME = IND_COL.INDEX_NAME | ||
| 381 | LEFT JOIN ALL_CONSTRAINTS CON | ||
| 382 | ON CON.OWNER = IND_COL.INDEX_OWNER | ||
| 383 | AND CON.INDEX_NAME = IND_COL.INDEX_NAME | ||
| 384 | SQL; | ||
| 385 | |||
| 386 | $conditions = ['IND_COL.INDEX_OWNER = :OWNER']; | ||
| 387 | $params = ['OWNER' => $databaseName]; | ||
| 388 | |||
| 389 | if ($tableName !== null) { | ||
| 390 | $conditions[] = 'IND_COL.TABLE_NAME = :TABLE_NAME'; | ||
| 391 | $params['TABLE_NAME'] = $tableName; | ||
| 392 | } | ||
| 393 | |||
| 394 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY IND_COL.TABLE_NAME, IND_COL.INDEX_NAME' | ||
| 395 | . ', IND_COL.COLUMN_POSITION'; | ||
| 396 | |||
| 397 | return $this->connection->executeQuery($sql, $params); | ||
| 398 | } | ||
| 399 | |||
| 400 | protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result | ||
| 401 | { | ||
| 402 | $sql = 'SELECT'; | ||
| 403 | |||
| 404 | if ($tableName === null) { | ||
| 405 | $sql .= ' COLS.TABLE_NAME,'; | ||
| 406 | } | ||
| 407 | |||
| 408 | $sql .= <<<'SQL' | ||
| 409 | ALC.CONSTRAINT_NAME, | ||
| 410 | ALC.DELETE_RULE, | ||
| 411 | COLS.COLUMN_NAME LOCAL_COLUMN, | ||
| 412 | COLS.POSITION, | ||
| 413 | R_COLS.TABLE_NAME REFERENCES_TABLE, | ||
| 414 | R_COLS.COLUMN_NAME FOREIGN_COLUMN | ||
| 415 | FROM ALL_CONS_COLUMNS COLS | ||
| 416 | LEFT JOIN ALL_CONSTRAINTS ALC ON ALC.OWNER = COLS.OWNER AND ALC.CONSTRAINT_NAME = COLS.CONSTRAINT_NAME | ||
| 417 | LEFT JOIN ALL_CONS_COLUMNS R_COLS ON R_COLS.OWNER = ALC.R_OWNER AND | ||
| 418 | R_COLS.CONSTRAINT_NAME = ALC.R_CONSTRAINT_NAME AND | ||
| 419 | R_COLS.POSITION = COLS.POSITION | ||
| 420 | SQL; | ||
| 421 | |||
| 422 | $conditions = ["ALC.CONSTRAINT_TYPE = 'R'", 'COLS.OWNER = :OWNER']; | ||
| 423 | $params = ['OWNER' => $databaseName]; | ||
| 424 | |||
| 425 | if ($tableName !== null) { | ||
| 426 | $conditions[] = 'COLS.TABLE_NAME = :TABLE_NAME'; | ||
| 427 | $params['TABLE_NAME'] = $tableName; | ||
| 428 | } | ||
| 429 | |||
| 430 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY COLS.TABLE_NAME, COLS.CONSTRAINT_NAME' | ||
| 431 | . ', COLS.POSITION'; | ||
| 432 | |||
| 433 | return $this->connection->executeQuery($sql, $params); | ||
| 434 | } | ||
| 435 | |||
| 436 | /** | ||
| 437 | * {@inheritDoc} | ||
| 438 | */ | ||
| 439 | protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array | ||
| 440 | { | ||
| 441 | $sql = 'SELECT TABLE_NAME, COMMENTS'; | ||
| 442 | |||
| 443 | $conditions = ['OWNER = :OWNER']; | ||
| 444 | $params = ['OWNER' => $databaseName]; | ||
| 445 | |||
| 446 | if ($tableName !== null) { | ||
| 447 | $conditions[] = 'TABLE_NAME = :TABLE_NAME'; | ||
| 448 | $params['TABLE_NAME'] = $tableName; | ||
| 449 | } | ||
| 450 | |||
| 451 | $sql .= ' FROM ALL_TAB_COMMENTS WHERE ' . implode(' AND ', $conditions); | ||
| 452 | |||
| 453 | /** @var array<string,array<string,mixed>> $metadata */ | ||
| 454 | $metadata = $this->connection->executeQuery($sql, $params) | ||
| 455 | ->fetchAllAssociativeIndexed(); | ||
| 456 | |||
| 457 | $tableOptions = []; | ||
| 458 | foreach ($metadata as $table => $data) { | ||
| 459 | $data = array_change_key_case($data, CASE_LOWER); | ||
| 460 | |||
| 461 | $tableOptions[$table] = [ | ||
| 462 | 'comment' => $data['comments'], | ||
| 463 | ]; | ||
| 464 | } | ||
| 465 | |||
| 466 | return $tableOptions; | ||
| 467 | } | ||
| 468 | |||
| 469 | protected function normalizeName(string $name): string | ||
| 470 | { | ||
| 471 | $identifier = new Identifier($name); | ||
| 472 | |||
| 473 | return $identifier->isQuoted() ? $identifier->getName() : strtoupper($name); | ||
| 474 | } | ||
| 475 | } | ||
