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