Failed Conditions
Pull Request — master (#3018)
by Gabriel
64:18
created

MySqlSchemaManager::getMariaDb1027ColumnDefault()   C

Complexity

Conditions 7
Paths 6

Size

Total Lines 23
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 27.6718

Importance

Changes 0
Metric Value
dl 0
loc 23
ccs 3
cts 12
cp 0.25
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 16
nc 6
nop 2
crap 27.6718
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\DBAL\Schema;
21
22
use Doctrine\DBAL\Platforms\MariaDb1027Platform;
23
use Doctrine\DBAL\Platforms\MySqlPlatform;
24
use Doctrine\DBAL\Types\Type;
25
use const CASE_LOWER;
26
use function array_change_key_case;
27
use function array_shift;
28
use function array_values;
29
use function end;
30
use function preg_match;
31
use function preg_replace;
32
use function str_replace;
33
use function stripslashes;
34
use function strpos;
35
use function strtok;
36
use function strtolower;
37
38
/**
39
 * Schema manager for the MySql RDBMS.
40
 *
41
 */
42
class MySqlSchemaManager extends AbstractSchemaManager
43
{
44
    /**
45
     * {@inheritdoc}
46
     */
47
    protected function _getPortableViewDefinition($view)
48
    {
49
        return new View($view['TABLE_NAME'], $view['VIEW_DEFINITION']);
50
    }
51
52
    /**
53
     * {@inheritdoc}
54
     */
55
    protected function _getPortableTableDefinition($table)
56
    {
57
        return array_shift($table);
58
    }
59
60
    /**
61
     * {@inheritdoc}
62
     */
63
    protected function _getPortableUserDefinition($user)
64
    {
65
        return [
66
            'user' => $user['User'],
67
            'password' => $user['Password'],
68
        ];
69
    }
70
71
    /**
72
     * {@inheritdoc}
73
     */
74
    protected function _getPortableTableIndexesList($tableIndexes, $tableName = null)
75
    {
76
        foreach ($tableIndexes as $k => $v) {
77
            $v = array_change_key_case($v, CASE_LOWER);
78
            if ($v['key_name'] === 'PRIMARY') {
79
                $v['primary'] = true;
80
            } else {
81
                $v['primary'] = false;
82
            }
83
            if (strpos($v['index_type'], 'FULLTEXT') !== false) {
84
                $v['flags'] = ['FULLTEXT'];
85
            } elseif (strpos($v['index_type'], 'SPATIAL') !== false) {
86
                $v['flags'] = ['SPATIAL'];
87
            }
88
            $tableIndexes[$k] = $v;
89
        }
90
91
        return parent::_getPortableTableIndexesList($tableIndexes, $tableName);
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97
    protected function _getPortableSequenceDefinition($sequence)
98
    {
99
        return end($sequence);
100
    }
101
102
    /**
103
     * {@inheritdoc}
104
     */
105
    protected function _getPortableDatabaseDefinition($database)
106
    {
107
        return $database['Database'];
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     */
113
    protected function _getPortableTableColumnDefinition($tableColumn)
114
    {
115
        $tableColumn = array_change_key_case($tableColumn, CASE_LOWER);
116
117
        $dbType = strtolower($tableColumn['type']);
118
        $dbType = strtok($dbType, '(), ');
119
        $length = $tableColumn['length'] ?? strtok('(), ');
120
121
        $fixed = null;
122
123
        if (! isset($tableColumn['name'])) {
124
            $tableColumn['name'] = '';
125
        }
126
127
        $scale     = null;
128
        $precision = null;
129
130
        $type = $this->_platform->getDoctrineTypeMapping($dbType);
131
132
        // In cases where not connected to a database DESCRIBE $table does not return 'Comment'
133
        if (isset($tableColumn['comment'])) {
134
            $type                   = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type);
135
            $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type);
136
        }
137
138
        switch ($dbType) {
139
            case 'char':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
140
            case 'binary':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
141
                $fixed = true;
142
                break;
143
            case 'float':
144
            case 'double':
145
            case 'real':
146
            case 'numeric':
147
            case 'decimal':
148
                if (preg_match('([A-Za-z]+\(([0-9]+)\,([0-9]+)\))', $tableColumn['type'], $match)) {
149
                    $precision = $match[1];
150
                    $scale     = $match[2];
151
                    $length    = null;
152
                }
153
                break;
154
            case 'tinytext':
155
                $length = MySqlPlatform::LENGTH_LIMIT_TINYTEXT;
156
                break;
157
            case 'text':
158
                $length = MySqlPlatform::LENGTH_LIMIT_TEXT;
159
                break;
160
            case 'mediumtext':
161
                $length = MySqlPlatform::LENGTH_LIMIT_MEDIUMTEXT;
162
                break;
163
            case 'tinyblob':
164
                $length = MySqlPlatform::LENGTH_LIMIT_TINYBLOB;
165
                break;
166
            case 'blob':
167
                $length = MySqlPlatform::LENGTH_LIMIT_BLOB;
168
                break;
169
            case 'mediumblob':
170
                $length = MySqlPlatform::LENGTH_LIMIT_MEDIUMBLOB;
171
                break;
172
            case 'tinyint':
173
            case 'smallint':
174
            case 'mediumint':
175
            case 'int':
176
            case 'integer':
177
            case 'bigint':
178
            case 'year':
179
                $length = null;
180
                break;
181
        }
182
183
        if ($this->_platform instanceof MariaDb1027Platform) {
184
            $columnDefault = $this->getMariaDb1027ColumnDefault($this->_platform, $tableColumn['default']);
185
        } else {
186
            $columnDefault = $tableColumn['default'];
187
        }
188
189
        $options = [
190
            'length'        => $length !== null ? (int) $length : null,
191
            'unsigned'      => strpos($tableColumn['type'], 'unsigned') !== false,
192
            'fixed'         => (bool) $fixed,
193
            'default'       => $columnDefault,
194
            'notnull'       => $tableColumn['null'] !== 'YES',
195
            'scale'         => null,
196
            'precision'     => null,
197
            'autoincrement' => strpos($tableColumn['extra'], 'auto_increment') !== false,
198
            'comment'       => isset($tableColumn['comment']) && $tableColumn['comment'] !== ''
199
                ? $tableColumn['comment']
200
                : null,
201
        ];
202
203
        if ($scale !== null && $precision !== null) {
204
            $options['scale']     = (int) $scale;
205
            $options['precision'] = (int) $precision;
206
        }
207
208
        $column = new Column($tableColumn['field'], Type::getType($type), $options);
209
210
        if (isset($tableColumn['collation'])) {
211
            $column->setPlatformOption('collation', $tableColumn['collation']);
212
        }
213
214
        return $column;
215
    }
216
217
    /**
218
     * Return Doctrine/Mysql-compatible column default values for MariaDB 10.2.7+ servers.
219
     *
220
     * - Since MariaDb 10.2.7 column defaults stored in information_schema are now quoted
221
     *   to distinguish them from expressions (see MDEV-10134).
222
     * - CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE are stored in information_schema
223
     *   as current_timestamp(), currdate(), currtime()
224
     * - Quoted 'NULL' is not enforced by Maria, it is technically possible to have
225
     *   null in some circumstances (see https://jira.mariadb.org/browse/MDEV-14053)
226
     * - \' is always stored as '' in information_schema (normalized)
227
     *
228
     * @link https://mariadb.com/kb/en/library/information-schema-columns-table/
229
     * @link https://jira.mariadb.org/browse/MDEV-13132
230
     *
231
     * @param null|string $columnDefault default value as stored in information_schema for MariaDB >= 10.2.7
232
     */
233
    private function getMariaDb1027ColumnDefault(MariaDb1027Platform $platform, ?string $columnDefault) : ?string
234
    {
235
        if ($columnDefault === 'NULL' || $columnDefault === null) {
236
            return null;
237
        }
238
        if ($columnDefault[0] === "'") {
239
            return stripslashes(
240
                str_replace(
241
                    "''",
242
                    "'",
243
                    preg_replace('/^\'(.*)\'$/', '$1', $columnDefault)
244
                )
245
            );
246
        }
247
        switch ($columnDefault) {
248
            case 'current_timestamp()':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
249
                return $platform->getCurrentTimestampSQL();
250
            case 'curdate()':
251
                return $platform->getCurrentDateSQL();
252 1
            case 'curtime()':
253
                return $platform->getCurrentTimeSQL();
254 1
        }
255 1
        return $columnDefault;
256 1
    }
257 1
258 1
    /**
259 1
     * {@inheritdoc}
260
     */
261 1
    protected function _getPortableTableForeignKeysList($tableForeignKeys)
262 1
    {
263
        $list = [];
264
        foreach ($tableForeignKeys as $value) {
265 1
            $value = array_change_key_case($value, CASE_LOWER);
266 1
            if (! isset($list[$value['constraint_name']])) {
267
                if (! isset($value['delete_rule']) || $value['delete_rule'] === 'RESTRICT') {
268
                    $value['delete_rule'] = null;
269 1
                }
270 1
                if (! isset($value['update_rule']) || $value['update_rule'] === 'RESTRICT') {
271 1
                    $value['update_rule'] = null;
272
                }
273
274 1
                $list[$value['constraint_name']] = [
275 1
                    'name' => $value['constraint_name'],
276
                    'local' => [],
277
                    'foreign' => [],
278 1
                    'foreignTable' => $value['referenced_table_name'],
279 1
                    'onDelete' => $value['delete_rule'],
280 1
                    'onUpdate' => $value['update_rule'],
281 1
                ];
282 1
            }
283 1
            $list[$value['constraint_name']]['local'][]   = $value['column_name'];
284 1
            $list[$value['constraint_name']]['foreign'][] = $value['referenced_column_name'];
285
        }
286 1
287 1
        $result = [];
288
        foreach ($list as $constraint) {
289
            $result[] = new ForeignKeyConstraint(
290
                array_values($constraint['local']),
291
                $constraint['foreignTable'],
292 1
                array_values($constraint['foreign']),
293
                $constraint['name'],
294
                [
295
                    'onDelete' => $constraint['onDelete'],
296
                    'onUpdate' => $constraint['onUpdate'],
297
                ]
298
            );
299
        }
300
301
        return $result;
302
    }
303
}
304