Completed
Push — master ( 15a9ef...af81c1 )
by Sergei
26:08
created

SqliteSchemaManager::parseColumnCommentFromSQL()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 7
cts 7
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 6
nc 3
nop 2
crap 3
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\DBALException;
23
use Doctrine\DBAL\FetchMode;
24
use Doctrine\DBAL\Types\StringType;
25
use Doctrine\DBAL\Types\TextType;
26
use Doctrine\DBAL\Types\Type;
27
use const CASE_LOWER;
28
use function array_change_key_case;
29
use function array_map;
30
use function array_reverse;
31
use function array_values;
32
use function explode;
33
use function file_exists;
34
use function preg_match;
35
use function preg_match_all;
36
use function preg_quote;
37
use function preg_replace;
38
use function rtrim;
39
use function sprintf;
40
use function str_replace;
41
use function strpos;
42
use function strtolower;
43
use function trim;
44
use function unlink;
45
use function usort;
46
47
/**
48
 * Sqlite SchemaManager.
49
 *
50
 * @author Konsta Vesterinen <[email protected]>
51
 * @author Lukas Smith <[email protected]> (PEAR MDB2 library)
52
 * @author Jonathan H. Wage <[email protected]>
53
 * @author Martin Hasoň <[email protected]>
54
 * @since  2.0
55
 */
56
class SqliteSchemaManager extends AbstractSchemaManager
57
{
58
    /**
59
     * {@inheritdoc}
60
     */
61 2
    public function dropDatabase($database)
62
    {
63 2
        if (file_exists($database)) {
64 2
            unlink($database);
65
        }
66 2
    }
67
68
    /**
69
     * {@inheritdoc}
70
     */
71 2
    public function createDatabase($database)
72
    {
73 2
        $params = $this->_conn->getParams();
74 2
        $driver = $params['driver'];
75
        $options = [
76 2
            'driver' => $driver,
77 2
            'path' => $database
78
        ];
79 2
        $conn = \Doctrine\DBAL\DriverManager::getConnection($options);
80 2
        $conn->connect();
81 2
        $conn->close();
82 2
    }
83
84
    /**
85
     * {@inheritdoc}
86
     */
87 1
    public function renameTable($name, $newName)
88
    {
89 1
        $tableDiff = new TableDiff($name);
90 1
        $tableDiff->fromTable = $this->listTableDetails($name);
91 1
        $tableDiff->newName = $newName;
92 1
        $this->alterTable($tableDiff);
93 1
    }
94
95
    /**
96
     * {@inheritdoc}
97
     */
98
    public function createForeignKey(ForeignKeyConstraint $foreignKey, $table)
99
    {
100
        $tableDiff = $this->getTableDiffForAlterForeignKey($foreignKey, $table);
101
        $tableDiff->addedForeignKeys[] = $foreignKey;
102
103
        $this->alterTable($tableDiff);
104
    }
105
106
    /**
107
     * {@inheritdoc}
108
     */
109
    public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table)
110
    {
111
        $tableDiff = $this->getTableDiffForAlterForeignKey($foreignKey, $table);
112
        $tableDiff->changedForeignKeys[] = $foreignKey;
113
114
        $this->alterTable($tableDiff);
115
    }
116
117
    /**
118
     * {@inheritdoc}
119
     */
120
    public function dropForeignKey($foreignKey, $table)
121
    {
122
        $tableDiff = $this->getTableDiffForAlterForeignKey($foreignKey, $table);
123
        $tableDiff->removedForeignKeys[] = $foreignKey;
124
125
        $this->alterTable($tableDiff);
126
    }
127
128
    /**
129
     * {@inheritdoc}
130
     */
131 1
    public function listTableForeignKeys($table, $database = null)
132
    {
133 1
        if (null === $database) {
134 1
            $database = $this->_conn->getDatabase();
135
        }
136 1
        $sql = $this->_platform->getListTableForeignKeysSQL($table, $database);
0 ignored issues
show
Unused Code introduced by
The call to Doctrine\DBAL\Platforms\...stTableForeignKeysSQL() has too many arguments starting with $database. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

136
        /** @scrutinizer ignore-call */ 
137
        $sql = $this->_platform->getListTableForeignKeysSQL($table, $database);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
137 1
        $tableForeignKeys = $this->_conn->fetchAll($sql);
138
139 1
        if ( ! empty($tableForeignKeys)) {
140 1
            $createSql = $this->_conn->fetchAll("SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type = 'table' AND name = '$table'");
141 1
            $createSql = $createSql[0]['sql'] ?? '';
142
143 1
            if (preg_match_all('#
144
                    (?:CONSTRAINT\s+([^\s]+)\s+)?
145
                    (?:FOREIGN\s+KEY[^\)]+\)\s*)?
146
                    REFERENCES\s+[^\s]+\s+(?:\([^\)]+\))?
147
                    (?:
148
                        [^,]*?
149
                        (NOT\s+DEFERRABLE|DEFERRABLE)
150
                        (?:\s+INITIALLY\s+(DEFERRED|IMMEDIATE))?
151
                    )?#isx',
152 1
                    $createSql, $match)) {
153
154 1
                $names = array_reverse($match[1]);
155 1
                $deferrable = array_reverse($match[2]);
156 1
                $deferred = array_reverse($match[3]);
157
            } else {
158
                $names = $deferrable = $deferred = [];
159
            }
160
161 1
            foreach ($tableForeignKeys as $key => $value) {
162 1
                $id = $value['id'];
163 1
                $tableForeignKeys[$key]['constraint_name'] = isset($names[$id]) && '' != $names[$id] ? $names[$id] : $id;
164 1
                $tableForeignKeys[$key]['deferrable'] = isset($deferrable[$id]) && 'deferrable' == strtolower($deferrable[$id]) ? true : false;
165 1
                $tableForeignKeys[$key]['deferred'] = isset($deferred[$id]) && 'deferred' == strtolower($deferred[$id]) ? true : false;
166
            }
167
        }
168
169 1
        return $this->_getPortableTableForeignKeysList($tableForeignKeys);
170
    }
171
172
    /**
173
     * {@inheritdoc}
174
     */
175 95
    protected function _getPortableTableDefinition($table)
176
    {
177 95
        return $table['name'];
178
    }
179
180
    /**
181
     * {@inheritdoc}
182
     *
183
     * @license New BSD License
184
     * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html
185
     */
186 65
    protected function _getPortableTableIndexesList($tableIndexes, $tableName=null)
187
    {
188 65
        $indexBuffer = [];
189
190
        // fetch primary
191 65
        $stmt = $this->_conn->executeQuery("PRAGMA TABLE_INFO ('$tableName')");
192 65
        $indexArray = $stmt->fetchAll(FetchMode::ASSOCIATIVE);
193
194
        usort($indexArray, function($a, $b) {
195 22
            if ($a['pk'] == $b['pk']) {
196 18
                return $a['cid'] - $b['cid'];
197
            }
198
199 12
            return $a['pk'] - $b['pk'];
200 65
        });
201 65
        foreach ($indexArray as $indexColumnRow) {
202 65
            if ($indexColumnRow['pk'] != "0") {
203 53
                $indexBuffer[] = [
204 53
                    'key_name' => 'primary',
205
                    'primary' => true,
206
                    'non_unique' => false,
207 65
                    'column_name' => $indexColumnRow['name']
208
                ];
209
            }
210
        }
211
212
        // fetch regular indexes
213 65
        foreach ($tableIndexes as $tableIndex) {
214
            // Ignore indexes with reserved names, e.g. autoindexes
215 8
            if (strpos($tableIndex['name'], 'sqlite_') !== 0) {
216 6
                $keyName = $tableIndex['name'];
217 6
                $idx = [];
218 6
                $idx['key_name'] = $keyName;
219 6
                $idx['primary'] = false;
220 6
                $idx['non_unique'] = $tableIndex['unique']?false:true;
221
222 6
                $stmt = $this->_conn->executeQuery("PRAGMA INDEX_INFO ('{$keyName}')");
223 6
                $indexArray = $stmt->fetchAll(FetchMode::ASSOCIATIVE);
224
225 6
                foreach ($indexArray as $indexColumnRow) {
226 6
                    $idx['column_name'] = $indexColumnRow['name'];
227 8
                    $indexBuffer[] = $idx;
228
                }
229
            }
230
        }
231
232 65
        return parent::_getPortableTableIndexesList($indexBuffer, $tableName);
233
    }
234
235
    /**
236
     * {@inheritdoc}
237
     */
238
    protected function _getPortableTableIndexDefinition($tableIndex)
239
    {
240
        return [
241
            'name' => $tableIndex['name'],
242
            'unique' => (bool) $tableIndex['unique']
243
        ];
244
    }
245
246
    /**
247
     * {@inheritdoc}
248
     */
249 72
    protected function _getPortableTableColumnList($table, $database, $tableColumns)
250
    {
251 72
        $list = parent::_getPortableTableColumnList($table, $database, $tableColumns);
252
253
        // find column with autoincrement
254 72
        $autoincrementColumn = null;
255 72
        $autoincrementCount = 0;
256
257 72
        foreach ($tableColumns as $tableColumn) {
258 72
            if ('0' != $tableColumn['pk']) {
259 56
                $autoincrementCount++;
260 56
                if (null === $autoincrementColumn && 'integer' == strtolower($tableColumn['type'])) {
261 72
                    $autoincrementColumn = $tableColumn['name'];
262
                }
263
            }
264
        }
265
266 72
        if (1 == $autoincrementCount && null !== $autoincrementColumn) {
0 ignored issues
show
introduced by
The condition null !== $autoincrementColumn is always false.
Loading history...
267 55
            foreach ($list as $column) {
268 55
                if ($autoincrementColumn == $column->getName()) {
269 55
                    $column->setAutoincrement(true);
270
                }
271
            }
272
        }
273
274
        // inspect column collation and comments
275 72
        $createSql = $this->_conn->fetchAll("SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type = 'table' AND name = '$table'");
276 72
        $createSql = $createSql[0]['sql'] ?? '';
277
278 72
        foreach ($list as $columnName => $column) {
279 72
            $type = $column->getType();
280
281 72
            if ($type instanceof StringType || $type instanceof TextType) {
282 16
                $column->setPlatformOption('collation', $this->parseColumnCollationFromSQL($columnName, $createSql) ?: 'BINARY');
283
            }
284
285 72
            $comment = $this->parseColumnCommentFromSQL($columnName, $createSql);
286
287 72
            if ($comment !== null) {
288 16
                $type = $this->extractDoctrineTypeFromComment($comment, null);
289
290 16
                if (null !== $type) {
291 4
                    $column->setType(Type::getType($type));
292
293 4
                    $comment = $this->removeDoctrineTypeFromComment($comment, $type);
294
                }
295
296 72
                $column->setComment($comment);
297
            }
298
        }
299
300 72
        return $list;
301
    }
302
303
    /**
304
     * {@inheritdoc}
305
     */
306 72
    protected function _getPortableTableColumnDefinition($tableColumn)
307
    {
308 72
        $parts = explode('(', $tableColumn['type']);
309 72
        $tableColumn['type'] = trim($parts[0]);
310 72
        if (isset($parts[1])) {
311 12
            $length = trim($parts[1], ')');
312 12
            $tableColumn['length'] = $length;
313
        }
314
315 72
        $dbType   = strtolower($tableColumn['type']);
316 72
        $length   = $tableColumn['length'] ?? null;
317 72
        $unsigned = false;
318
319 72
        if (strpos($dbType, ' unsigned') !== false) {
320 1
            $dbType = str_replace(' unsigned', '', $dbType);
321 1
            $unsigned = true;
322
        }
323
324 72
        $fixed = false;
325 72
        $type = $this->_platform->getDoctrineTypeMapping($dbType);
326 72
        $default = $tableColumn['dflt_value'];
327 72
        if ($default == 'NULL') {
328 4
            $default = null;
329
        }
330 72
        if ($default !== null) {
331
            // SQLite returns strings wrapped in single quotes, so we need to strip them
332 6
            $default = preg_replace("/^'(.*)'$/", '\1', $default);
333
        }
334 72
        $notnull = (bool) $tableColumn['notnull'];
335
336 72
        if ( ! isset($tableColumn['name'])) {
337
            $tableColumn['name'] = '';
338
        }
339
340 72
        $precision = null;
341 72
        $scale = null;
342
343
        switch ($dbType) {
344 72
            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...
345 4
                $fixed = true;
346 4
                break;
347 71
            case 'float':
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...
348 71
            case 'double':
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...
349 71
            case 'real':
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...
350 71
            case 'decimal':
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...
351 71
            case 'numeric':
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...
352 4
                if (isset($tableColumn['length'])) {
353 4
                    if (strpos($tableColumn['length'], ',') === false) {
354
                        $tableColumn['length'] .= ",0";
355
                    }
356 4
                    list($precision, $scale) = array_map('trim', explode(',', $tableColumn['length']));
357
                }
358 4
                $length = null;
359 4
                break;
360
        }
361
362
        $options = [
363 72
            'length'   => $length,
364 72
            'unsigned' => (bool) $unsigned,
365 72
            'fixed'    => $fixed,
366 72
            'notnull'  => $notnull,
367 72
            'default'  => $default,
368 72
            'precision' => $precision,
369 72
            'scale'     => $scale,
370
            'autoincrement' => false,
371
        ];
372
373 72
        return new Column($tableColumn['name'], \Doctrine\DBAL\Types\Type::getType($type), $options);
374
    }
375
376
    /**
377
     * {@inheritdoc}
378
     */
379 1
    protected function _getPortableViewDefinition($view)
380
    {
381 1
        return new View($view['name'], $view['sql']);
382
    }
383
384
    /**
385
     * {@inheritdoc}
386
     */
387 1
    protected function _getPortableTableForeignKeysList($tableForeignKeys)
388
    {
389 1
        $list = [];
390 1
        foreach ($tableForeignKeys as $value) {
391 1
            $value = array_change_key_case($value, CASE_LOWER);
392 1
            $name = $value['constraint_name'];
393 1
            if ( ! isset($list[$name])) {
394 1
                if ( ! isset($value['on_delete']) || $value['on_delete'] == "RESTRICT") {
395
                    $value['on_delete'] = null;
396
                }
397 1
                if ( ! isset($value['on_update']) || $value['on_update'] == "RESTRICT") {
398
                    $value['on_update'] = null;
399
                }
400
401 1
                $list[$name] = [
402 1
                    'name' => $name,
403
                    'local' => [],
404
                    'foreign' => [],
405 1
                    'foreignTable' => $value['table'],
406 1
                    'onDelete' => $value['on_delete'],
407 1
                    'onUpdate' => $value['on_update'],
408 1
                    'deferrable' => $value['deferrable'],
409 1
                    'deferred'=> $value['deferred'],
410
                ];
411
            }
412 1
            $list[$name]['local'][] = $value['from'];
413 1
            $list[$name]['foreign'][] = $value['to'];
414
        }
415
416 1
        $result = [];
417 1
        foreach ($list as $constraint) {
418 1
            $result[] = new ForeignKeyConstraint(
419 1
                array_values($constraint['local']), $constraint['foreignTable'],
420 1
                array_values($constraint['foreign']), $constraint['name'],
421
                [
422 1
                    'onDelete' => $constraint['onDelete'],
423 1
                    'onUpdate' => $constraint['onUpdate'],
424 1
                    'deferrable' => $constraint['deferrable'],
425 1
                    'deferred'=> $constraint['deferred'],
426
                ]
427
            );
428
        }
429
430 1
        return $result;
431
    }
432
433
    /**
434
     * @param \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey
435
     * @param \Doctrine\DBAL\Schema\Table|string         $table
436
     *
437
     * @return \Doctrine\DBAL\Schema\TableDiff
438
     *
439
     * @throws \Doctrine\DBAL\DBALException
440
     */
441
    private function getTableDiffForAlterForeignKey(ForeignKeyConstraint $foreignKey, $table)
0 ignored issues
show
Unused Code introduced by
The parameter $foreignKey is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

441
    private function getTableDiffForAlterForeignKey(/** @scrutinizer ignore-unused */ ForeignKeyConstraint $foreignKey, $table)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
442
    {
443
        if ( ! $table instanceof Table) {
444
            $tableDetails = $this->tryMethod('listTableDetails', $table);
445
            if (false === $table) {
0 ignored issues
show
introduced by
The condition false === $table is always false.
Loading history...
446
                throw new DBALException(sprintf('Sqlite schema manager requires to modify foreign keys table definition "%s".', $table));
447
            }
448
449
            $table = $tableDetails;
450
        }
451
452
        $tableDiff = new TableDiff($table->getName());
453
        $tableDiff->fromTable = $table;
0 ignored issues
show
Documentation Bug introduced by
It seems like $table can also be of type false. However, the property $fromTable is declared as type Doctrine\DBAL\Schema\Table. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
454
455
        return $tableDiff;
456
    }
457
458 288
    private function parseColumnCollationFromSQL(string $column, string $sql) : ?string
459
    {
460 288
        $pattern = '{(?:\W' . preg_quote($column) . '\W|\W' . preg_quote($this->_platform->quoteSingleIdentifier($column))
461 288
            . '\W)[^,(]+(?:\([^()]+\)[^,]*)?(?:(?:DEFAULT|CHECK)\s*(?:\(.*?\))?[^,]*)*COLLATE\s+["\']?([^\s,"\')]+)}is';
462
463 288
        if (preg_match($pattern, $sql, $match) !== 1) {
464 101
            return null;
465
        }
466
467 190
        return $match[1];
468
    }
469
470 446
    private function parseColumnCommentFromSQL(string $column, string $sql) : ?string
471
    {
472 446
        $pattern = '{[\s(,](?:\W' . preg_quote($this->_platform->quoteSingleIdentifier($column)) . '\W|\W' . preg_quote($column)
473 446
            . '\W)(?:\(.*?\)|[^,(])*?,?((?:(?!\n))(?:\s*--[^\n]*\n?)+)}i';
474
475 446
        if (preg_match($pattern, $sql, $match) !== 1) {
476 254
            return null;
477
        }
478
479 203
        $comment = preg_replace('{^\s*--}m', '', rtrim($match[1], "\n"));
480
481 203
        return '' === $comment ? null : $comment;
482
    }
483
}
484