_getPortableTableColumnDefinition()   F
last analyzed

Complexity

Conditions 25
Paths 6336

Size

Total Lines 89
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 52
CRAP Score 25.1011

Importance

Changes 0
Metric Value
cc 25
eloc 56
nc 6336
nop 1
dl 0
loc 89
ccs 52
cts 55
cp 0.9455
crap 25.1011
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\DBAL\Schema;
6
7
use Doctrine\DBAL\DBALException;
8
use Doctrine\DBAL\Driver\DriverException;
9
use Doctrine\DBAL\Platforms\OraclePlatform;
10
use Doctrine\DBAL\Types\Type;
11
use Throwable;
12
use function array_change_key_case;
13
use function array_values;
14
use function assert;
15
use function preg_match;
16
use function sprintf;
17
use function str_replace;
18
use function strpos;
19
use function strtolower;
20
use function strtoupper;
21
use function trim;
22
use const CASE_LOWER;
23
24
/**
25
 * Oracle Schema Manager.
26
 */
27
class OracleSchemaManager extends AbstractSchemaManager
28
{
29 2
    public function dropDatabase(string $database) : void
30
    {
31
        try {
32 2
            parent::dropDatabase($database);
33 2
        } catch (DBALException $exception) {
34 2
            $exception = $exception->getPrevious();
35 2
            assert($exception instanceof Throwable);
36
37 2
            if (! $exception instanceof DriverException) {
38
                throw $exception;
39
            }
40
41
            // If we have a error code 1940 (ORA-01940), the drop database operation failed
42
            // because of active connections on the database.
43
            // To force dropping the database, we first have to close all active connections
44
            // on that database and issue the drop database operation again.
45 2
            if ($exception->getCode() !== 1940) {
46 2
                throw $exception;
47
            }
48
49
            $this->killUserSessions($database);
50
51
            parent::dropDatabase($database);
52
        }
53 1
    }
54
55
    /**
56
     * {@inheritdoc}
57
     */
58 1
    protected function _getPortableViewDefinition(array $view) : View
59
    {
60 1
        $view = array_change_key_case($view, CASE_LOWER);
61
62 1
        return new View($this->getQuotedIdentifierName($view['view_name']), $view['text']);
63
    }
64
65
    /**
66
     * {@inheritdoc}
67
     */
68
    protected function _getPortableUserDefinition(array $user) : array
69
    {
70
        $user = array_change_key_case($user, CASE_LOWER);
71
72
        return [
73
            'user' => $user['username'],
74
        ];
75
    }
76
77
    /**
78
     * {@inheritdoc}
79
     */
80 13
    protected function _getPortableTableDefinition(array $table) : string
81
    {
82 13
        $table = array_change_key_case($table, CASE_LOWER);
83
84 13
        return $this->getQuotedIdentifierName($table['table_name']);
85
    }
86
87
    /**
88
     * {@inheritdoc}
89
     *
90
     * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html
91
     */
92 51
    protected function _getPortableTableIndexesList(array $tableIndexRows, string $tableName) : array
93
    {
94 51
        $indexBuffer = [];
95 51
        foreach ($tableIndexRows as $tableIndex) {
96 16
            $tableIndex = array_change_key_case($tableIndex, CASE_LOWER);
97
98 16
            $keyName = strtolower($tableIndex['name']);
99 16
            $buffer  = [];
100
101 16
            if ($tableIndex['is_primary'] === 'P') {
102 14
                $keyName              = 'primary';
103 14
                $buffer['primary']    = true;
104 14
                $buffer['non_unique'] = false;
105
            } else {
106 13
                $buffer['primary']    = false;
107 13
                $buffer['non_unique'] = ! $tableIndex['is_unique'];
108
            }
109
110 16
            $buffer['key_name']    = $keyName;
111 16
            $buffer['column_name'] = $this->getQuotedIdentifierName($tableIndex['column_name']);
112 16
            $indexBuffer[]         = $buffer;
113
        }
114
115 51
        return parent::_getPortableTableIndexesList($indexBuffer, $tableName);
116
    }
117
118
    /**
119
     * {@inheritdoc}
120
     */
121 54
    protected function _getPortableTableColumnDefinition(array $tableColumn) : Column
122
    {
123 54
        $tableColumn = array_change_key_case($tableColumn, CASE_LOWER);
124
125 54
        $dbType = strtolower($tableColumn['data_type']);
126 54
        if (strpos($dbType, 'timestamp(') === 0) {
127 7
            if (strpos($dbType, 'with time zone') !== false) {
128 3
                $dbType = 'timestamptz';
129
            } else {
130 7
                $dbType = 'timestamp';
131
            }
132
        }
133
134 54
        $length = $precision = null;
135 54
        $scale  = 0;
136 54
        $fixed  = false;
137
138 54
        if (! isset($tableColumn['column_name'])) {
139
            $tableColumn['column_name'] = '';
140
        }
141
142
        // Default values returned from database sometimes have trailing spaces.
143 54
        if ($tableColumn['data_default'] !== null) {
144 28
            $tableColumn['data_default'] = trim($tableColumn['data_default']);
145
        }
146
147 54
        if ($tableColumn['data_default'] === '' || $tableColumn['data_default'] === 'NULL') {
148 25
            $tableColumn['data_default'] = null;
149
        }
150
151 54
        if ($tableColumn['data_default'] !== null) {
152
            // Default values returned from database are represented as literal expressions
153 27
            if (preg_match('/^\'(.*)\'$/s', $tableColumn['data_default'], $matches) === 1) {
154 26
                $tableColumn['data_default'] = str_replace("''", "'", $matches[1]);
155
            }
156
        }
157
158 54
        if ($tableColumn['data_precision'] !== null) {
159 52
            $precision = (int) $tableColumn['data_precision'];
160
        }
161
162 54
        if ($tableColumn['data_scale'] !== null) {
163 53
            $scale = (int) $tableColumn['data_scale'];
164
        }
165
166 54
        $type = $this->extractDoctrineTypeFromComment($tableColumn['comments'])
167 54
            ?? $this->_platform->getDoctrineTypeMapping($dbType);
168
169 54
        switch ($dbType) {
170 54
            case 'number':
171 52
                if ($precision === 20 && $scale === 0) {
172
                    $type = 'bigint';
173 52
                } elseif ($precision === 5 && $scale === 0) {
174
                    $type = 'smallint';
175 52
                } elseif ($precision === 1 && $scale === 0) {
176 1
                    $type = 'boolean';
177 51
                } elseif ($scale > 0) {
178 5
                    $type = 'decimal';
179
                }
180
181 52
                break;
182
183 42
            case 'varchar':
184 42
            case 'varchar2':
185 21
            case 'nvarchar2':
186 37
                $length = (int) $tableColumn['char_length'];
187 37
                break;
188
189 21
            case 'char':
190 19
            case 'nchar':
191 5
                $length = (int) $tableColumn['char_length'];
192 5
                $fixed  = true;
193 5
                break;
194
        }
195
196
        $options = [
197 54
            'notnull'    => $tableColumn['nullable'] === 'N',
198 54
            'fixed'      => $fixed,
199 54
            'default'    => $tableColumn['data_default'],
200 54
            'length'     => $length,
201 54
            'precision'  => $precision,
202 54
            'scale'      => $scale,
203
        ];
204
205 54
        if (isset($tableColumn['comments'])) {
206 16
            $options['comment'] = $tableColumn['comments'];
207
        }
208
209 54
        return new Column($this->getQuotedIdentifierName($tableColumn['column_name']), Type::getType($type), $options);
210
    }
211
212
    /**
213
     * {@inheritdoc}
214
     */
215 49
    protected function _getPortableTableForeignKeysList(array $tableForeignKeys) : array
216
    {
217 49
        $list = [];
218 49
        foreach ($tableForeignKeys as $value) {
219 12
            $value = array_change_key_case($value, CASE_LOWER);
220 12
            if (! isset($list[$value['constraint_name']])) {
221 12
                if ($value['delete_rule'] === 'NO ACTION') {
222 11
                    $value['delete_rule'] = null;
223
                }
224
225 12
                $list[$value['constraint_name']] = [
226 12
                    'name' => $this->getQuotedIdentifierName($value['constraint_name']),
227
                    'local' => [],
228
                    'foreign' => [],
229 12
                    'foreignTable' => $value['references_table'],
230 12
                    'onDelete' => $value['delete_rule'],
231
                ];
232
            }
233
234 12
            $localColumn   = $this->getQuotedIdentifierName($value['local_column']);
235 12
            $foreignColumn = $this->getQuotedIdentifierName($value['foreign_column']);
236
237 12
            $list[$value['constraint_name']]['local'][$value['position']]   = $localColumn;
238 12
            $list[$value['constraint_name']]['foreign'][$value['position']] = $foreignColumn;
239
        }
240
241 49
        $result = [];
242 49
        foreach ($list as $constraint) {
243 12
            $result[] = new ForeignKeyConstraint(
244 12
                array_values($constraint['local']),
245 12
                $this->getQuotedIdentifierName($constraint['foreignTable']),
246 12
                array_values($constraint['foreign']),
247 12
                $this->getQuotedIdentifierName($constraint['name']),
248 12
                ['onDelete' => $constraint['onDelete']]
249
            );
250
        }
251
252 49
        return $result;
253
    }
254
255
    /**
256
     * {@inheritdoc}
257
     */
258 5
    protected function _getPortableSequenceDefinition(array $sequence) : Sequence
259
    {
260 5
        $sequence = array_change_key_case($sequence, CASE_LOWER);
261
262 5
        return new Sequence(
263 5
            $this->getQuotedIdentifierName($sequence['sequence_name']),
264 5
            (int) $sequence['increment_by'],
265 5
            (int) $sequence['min_value']
266
        );
267
    }
268
269
    /**
270
     * {@inheritdoc}
271
     */
272 2
    protected function _getPortableDatabaseDefinition(array $database) : string
273
    {
274 2
        $database = array_change_key_case($database, CASE_LOWER);
275
276 2
        return $database['username'];
277
    }
278
279 2
    public function createDatabase(string $database) : void
280
    {
281 2
        $params   = $this->_conn->getParams();
282 2
        $username = $database;
283 2
        $password = $params['password'];
284
285 2
        $query = 'CREATE USER ' . $username . ' IDENTIFIED BY ' . $password;
286 2
        $this->_conn->executeUpdate($query);
287
288 2
        $query = 'GRANT DBA TO ' . $username;
289 2
        $this->_conn->executeUpdate($query);
290 2
    }
291
292 131
    public function dropAutoincrement(string $table) : bool
293
    {
294 131
        assert($this->_platform instanceof OraclePlatform);
295
296 131
        $sql = $this->_platform->getDropAutoincrementSql($table);
297 131
        foreach ($sql as $query) {
298 131
            $this->_conn->executeUpdate($query);
299
        }
300
301
        return true;
302
    }
303
304 131
    public function dropTable(string $name) : void
305
    {
306 131
        $this->tryMethod('dropAutoincrement', $name);
307
308 131
        parent::dropTable($name);
309 82
    }
310
311
    /**
312
     * Returns the quoted representation of the given identifier name.
313
     *
314
     * Quotes non-uppercase identifiers explicitly to preserve case
315
     * and thus make references to the particular identifier work.
316
     */
317 77
    private function getQuotedIdentifierName(string $identifier) : string
318
    {
319 77
        if (preg_match('/[a-z]/', $identifier) === 1) {
320 9
            return $this->_platform->quoteIdentifier($identifier);
321
        }
322
323 77
        return $identifier;
324
    }
325
326
    /**
327
     * Kills sessions connected with the given user.
328
     *
329
     * This is useful to force DROP USER operations which could fail because of active user sessions.
330
     *
331
     * @param string $user The name of the user to kill sessions for.
332
     */
333
    private function killUserSessions(string $user) : void
334
    {
335
        $sql = <<<SQL
336
SELECT
337
    s.sid,
338
    s.serial#
339
FROM
340
    gv\$session s,
341
    gv\$process p
342
WHERE
343
    s.username = ?
344
    AND p.addr(+) = s.paddr
345
SQL;
346
347
        $activeUserSessions = $this->_conn->fetchAll($sql, [strtoupper($user)]);
348
349
        foreach ($activeUserSessions as $activeUserSession) {
350
            $activeUserSession = array_change_key_case($activeUserSession, CASE_LOWER);
351
352
            $this->_execSql(
353
                sprintf(
354
                    "ALTER SYSTEM KILL SESSION '%s, %s' IMMEDIATE",
355
                    $activeUserSession['sid'],
356
                    $activeUserSession['serial#']
357
                )
358
            );
359
        }
360
    }
361
362 46
    public function listTableDetails(string $tableName) : Table
363
    {
364 46
        $table = parent::listTableDetails($tableName);
365
366 46
        $platform = $this->_platform;
367 46
        assert($platform instanceof OraclePlatform);
368 46
        $sql = $platform->getListTableCommentsSQL($tableName);
369
370 46
        $tableOptions = $this->_conn->fetchAssoc($sql);
371
372 46
        if ($tableOptions !== false) {
373 46
            $table->addOption('comment', $tableOptions['COMMENTS']);
374
        }
375
376 46
        return $table;
377
    }
378
}
379