Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

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