Completed
Pull Request — master (#3189)
by Jarek
61:30
created

MySqlSchemaManager::_getPortableViewDefinition()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 1
crap 1
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
 * @author Konsta Vesterinen <[email protected]>
42
 * @author Lukas Smith <[email protected]> (PEAR MDB2 library)
43
 * @author Roman Borschel <[email protected]>
44
 * @author Benjamin Eberlei <[email protected]>
45
 * @since  2.0
46
 */
47
class MySqlSchemaManager extends AbstractSchemaManager
48
{
49
    /**
50
     * {@inheritdoc}
51
     */
52 8
    protected function _getPortableViewDefinition($view)
53
    {
54 8
        return new View($view['TABLE_NAME'], $view['VIEW_DEFINITION']);
55
    }
56
57
    /**
58
     * {@inheritdoc}
59
     */
60 582
    protected function _getPortableTableDefinition($table)
61
    {
62 582
        return array_shift($table);
63
    }
64
65
    /**
66
     * {@inheritdoc}
67
     */
68
    protected function _getPortableUserDefinition($user)
69
    {
70
        return [
71
            'user' => $user['User'],
72
            'password' => $user['Password'],
73
        ];
74
    }
75
76
    /**
77
     * {@inheritdoc}
78
     */
79 294
    protected function _getPortableTableIndexesList($tableIndexes, $tableName = null)
80
    {
81 294
        foreach ($tableIndexes as $k => $v) {
82 160
            $v = array_change_key_case($v, CASE_LOWER);
83 160
            if ($v['key_name'] === 'PRIMARY') {
84 128
                $v['primary'] = true;
85
            } else {
86 104
                $v['primary'] = false;
87
            }
88 160
            if (strpos($v['index_type'], 'FULLTEXT') !== false) {
89 24
                $v['flags'] = ['FULLTEXT'];
90 152
            } elseif (strpos($v['index_type'], 'SPATIAL') !== false) {
91 24
                $v['flags'] = ['SPATIAL'];
92
            }
93 160
            $tableIndexes[$k] = $v;
94
        }
95
96 294
        return parent::_getPortableTableIndexesList($tableIndexes, $tableName);
97
    }
98
99
    /**
100
     * {@inheritdoc}
101
     */
102
    protected function _getPortableSequenceDefinition($sequence)
103
    {
104
        return end($sequence);
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     */
110 16
    protected function _getPortableDatabaseDefinition($database)
111
    {
112 16
        return $database['Database'];
113
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118 358
    protected function _getPortableTableColumnDefinition($tableColumn)
119
    {
120 358
        $tableColumn = array_change_key_case($tableColumn, CASE_LOWER);
121
122 358
        $dbType = strtolower($tableColumn['type']);
123 358
        $dbType = strtok($dbType, '(), ');
124 358
        $length = $tableColumn['length'] ?? strtok('(), ');
125
126 358
        $fixed = null;
127
128 358
        if ( ! isset($tableColumn['name'])) {
129 358
            $tableColumn['name'] = '';
130
        }
131
132 358
        $scale     = null;
133 358
        $precision = null;
134
135 358
        $type = $this->_platform->getDoctrineTypeMapping($dbType);
136
137
        // In cases where not connected to a database DESCRIBE $table does not return 'Comment'
138 358
        if (isset($tableColumn['comment'])) {
139 358
            $type                   = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type);
140 358
            $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type);
141
        }
142
143
        switch ($dbType) {
144 358
            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...
145 342
            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...
146 48
                $fixed = true;
147 48
                break;
148 342
            case 'float':
149 342
            case 'double':
150 334
            case 'real':
151 334
            case 'numeric':
152 334
            case 'decimal':
153 56
                if (preg_match('([A-Za-z]+\(([0-9]+)\,([0-9]+)\))', $tableColumn['type'], $match)) {
154 48
                    $precision = $match[1];
155 48
                    $scale     = $match[2];
156 48
                    $length    = null;
157
                }
158 56
                break;
159 326
            case 'tinytext':
160 24
                $length = MySqlPlatform::LENGTH_LIMIT_TINYTEXT;
161 24
                break;
162 326
            case 'text':
163 24
                $length = MySqlPlatform::LENGTH_LIMIT_TEXT;
164 24
                break;
165 326
            case 'mediumtext':
166 24
                $length = MySqlPlatform::LENGTH_LIMIT_MEDIUMTEXT;
167 24
                break;
168 326
            case 'tinyblob':
169
                $length = MySqlPlatform::LENGTH_LIMIT_TINYBLOB;
170
                break;
171 326
            case 'blob':
172 24
                $length = MySqlPlatform::LENGTH_LIMIT_BLOB;
173 24
                break;
174 326
            case 'mediumblob':
175 24
                $length = MySqlPlatform::LENGTH_LIMIT_MEDIUMBLOB;
176 24
                break;
177 326
            case 'tinyint':
178 326
            case 'smallint':
179 326
            case 'mediumint':
180 326
            case 'int':
181 174
            case 'integer':
182 174
            case 'bigint':
183 174
            case 'year':
184 264
                $length = null;
185 264
                break;
186
        }
187
188 358
        if ($this->_platform instanceof MariaDb1027Platform) {
189 88
            $columnDefault = $this->getMariaDb1027ColumnDefault($this->_platform, $tableColumn['default']);
190
        } else {
191 270
            if ($dbType === 'text' || $dbType === 'blob') {
192
                // MySQL (all versions) and MariaDB (<10.2.1) does not support default value for TEXT and BLOB types
193
                $columnDefault = null;
194
            } else {
195 358
                $columnDefault = $tableColumn['default'];
196 358
            }
197 358
        }
198 358
199 358
        $options = [
200
            'length'        => $length !== null ? (int) $length : null,
201
            'unsigned'      => strpos($tableColumn['type'], 'unsigned') !== false,
202 358
            'fixed'         => (bool) $fixed,
203 358
            'default'       => $columnDefault,
204 112
            'notnull'       => $tableColumn['null'] !== 'YES',
205
            'scale'         => null,
206
            'precision'     => null,
207
            'autoincrement' => strpos($tableColumn['extra'], 'auto_increment') !== false,
208 358
            'comment'       => isset($tableColumn['comment']) && $tableColumn['comment'] !== ''
209 48
                ? $tableColumn['comment']
210 48
                : null,
211
        ];
212
213 358
        if ($scale !== null && $precision !== null) {
214
            $options['scale']     = (int) $scale;
215 358
            $options['precision'] = (int) $precision;
216 160
        }
217
218
        $column = new Column($tableColumn['field'], Type::getType($type), $options);
219 358
220
        if (isset($tableColumn['collation'])) {
221
            $column->setPlatformOption('collation', $tableColumn['collation']);
222
        }
223
224
        return $column;
225
    }
226
227
    /**
228
     * Return Doctrine/Mysql-compatible column default values for MariaDB 10.2.7+ servers.
229
     *
230
     * - Since MariaDb 10.2.7 column defaults stored in information_schema are now quoted
231
     *   to distinguish them from expressions (see MDEV-10134).
232
     * - CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE are stored in information_schema
233
     *   as current_timestamp(), currdate(), currtime()
234
     * - Quoted 'NULL' is not enforced by Maria, it is technically possible to have
235
     *   null in some circumstances (see https://jira.mariadb.org/browse/MDEV-14053)
236
     * - \' is always stored as '' in information_schema (normalized)
237
     *
238 88
     * @link https://mariadb.com/kb/en/library/information-schema-columns-table/
239
     * @link https://jira.mariadb.org/browse/MDEV-13132
240 88
     *
241 80
     * @param null|string $columnDefault default value as stored in information_schema for MariaDB >= 10.2.7
242
     */
243 20
    private function getMariaDb1027ColumnDefault(MariaDb1027Platform $platform, ?string $columnDefault) : ?string
244 16
    {
245 16
        if ($columnDefault === 'NULL' || $columnDefault === null) {
246 16
            return null;
247
        }
248
        if ($columnDefault[0] === "'") {
249
            return stripslashes(
250
                str_replace("''", "'",
251 12
                    preg_replace('/^\'(.*)\'$/', '$1', $columnDefault)
252 8
                )
253 10
            );
254 6
        }
255 10
        switch ($columnDefault) {
256 6
            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...
257
                return $platform->getCurrentTimestampSQL();
258 8
            case 'curdate()':
259
                return $platform->getCurrentDateSQL();
260
            case 'curtime()':
261
                return $platform->getCurrentTimeSQL();
262
        }
263
        return $columnDefault;
264 281
    }
265
266 281
    /**
267 281
     * {@inheritdoc}
268 75
     */
269 75
    protected function _getPortableTableForeignKeysList($tableForeignKeys)
270 75
    {
271 67
        $list = [];
272
        foreach ($tableForeignKeys as $value) {
273 75
            $value = array_change_key_case($value, CASE_LOWER);
274 75
            if ( ! isset($list[$value['constraint_name']])) {
275
                if ( ! isset($value['delete_rule']) || $value['delete_rule'] === "RESTRICT") {
276
                    $value['delete_rule'] = null;
277 75
                }
278 75
                if ( ! isset($value['update_rule']) || $value['update_rule'] === "RESTRICT") {
279
                    $value['update_rule'] = null;
280
                }
281 75
282 75
                $list[$value['constraint_name']] = [
283 75
                    'name' => $value['constraint_name'],
284
                    'local' => [],
285
                    'foreign' => [],
286 75
                    'foreignTable' => $value['referenced_table_name'],
287 75
                    'onDelete' => $value['delete_rule'],
288
                    'onUpdate' => $value['update_rule'],
289
                ];
290 281
            }
291 281
            $list[$value['constraint_name']]['local'][]   = $value['column_name'];
292 75
            $list[$value['constraint_name']]['foreign'][] = $value['referenced_column_name'];
293 75
        }
294 75
295 75
        $result = [];
296 75
        foreach ($list as $constraint) {
297
            $result[] = new ForeignKeyConstraint(
298 75
                array_values($constraint['local']),
299 75
                $constraint['foreignTable'],
300
                array_values($constraint['foreign']),
301
                $constraint['name'],
302
                [
303
                    'onDelete' => $constraint['onDelete'],
304 281
                    'onUpdate' => $constraint['onUpdate'],
305
                ]
306
            );
307
        }
308
309
        return $result;
310
    }
311
}
312