Skip to content

Commit b78617d

Browse files
authored
Fix column definition parsing in cases with brackets and escaped quotes (#1109)
1 parent b005f4d commit b78617d

5 files changed

Lines changed: 53 additions & 12 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@
177177
- Bug #1103: Fix view names' cache refreshing after "drop table" command execution (@vjik)
178178
- Chg #1103: Remove `AbstractCommand::refreshTableSchema()` method (@vjik)
179179
- Chg #1106: Remove parameters from `PdoConnectionInterface::getActivePdo()` method (@vjik)
180+
- Bug #1109: Fix column definition parsing in cases with brackets and escaped quotes (@vjik)
180181

181182
## 1.3.0 March 21, 2024
182183

src/Syntax/ColumnDefinitionParser.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class ColumnDefinitionParser
5656
*/
5757
public function parse(string $definition): array
5858
{
59-
preg_match('/^(\w*)(?:\(([^)]+)\))?(\[[\d\[\]]*\])?\s*/', $definition, $matches);
59+
preg_match("/^(\w*)(?:\(((?:'[^']*'|[^)])+)\))?(\[[\d\[\]]*\])?\s*/", $definition, $matches);
6060

6161
$type = strtolower($matches[1]);
6262
$info = ['type' => $type];
@@ -84,9 +84,14 @@ public function parse(string $definition): array
8484
*/
8585
protected function enumInfo(string $values): array
8686
{
87-
preg_match_all("/'([^']*)'/", $values, $matches);
87+
preg_match_all("/'((?:''|[^'])*)'/", $values, $matches);
8888

89-
return ['enumValues' => $matches[1]];
89+
$values = array_map(
90+
static fn(string $value): string => str_replace("''", "'", $value),
91+
$matches[1],
92+
);
93+
94+
return ['enumValues' => $values];
9095
}
9196

9297
/**

tests/Common/CommonColumnDefinitionParserTest.php

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@
99
use Yiisoft\Db\Syntax\ColumnDefinitionParser;
1010
use Yiisoft\Db\Tests\Provider\ColumnDefinitionParserProvider;
1111

12-
/**
13-
* @group db
14-
*/
1512
abstract class CommonColumnDefinitionParserTest extends TestCase
1613
{
1714
#[DataProviderExternal(ColumnDefinitionParserProvider::class, 'parse')]
@@ -22,8 +19,5 @@ public function testParse(string $definition, array $expected): void
2219
$this->assertSame($expected, $parser->parse($definition));
2320
}
2421

25-
protected function createColumnDefinitionParser(): ColumnDefinitionParser
26-
{
27-
return new ColumnDefinitionParser();
28-
}
22+
abstract protected function createColumnDefinitionParser(): ColumnDefinitionParser;
2923
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Db\Tests\Db\Syntax;
6+
7+
use Yiisoft\Db\Syntax\ColumnDefinitionParser;
8+
use Yiisoft\Db\Tests\Common\CommonColumnDefinitionParserTest;
9+
10+
/**
11+
* @group db
12+
*/
13+
final class ColumnDefinitionParserTest extends CommonColumnDefinitionParserTest
14+
{
15+
protected function createColumnDefinitionParser(): ColumnDefinitionParser
16+
{
17+
return new ColumnDefinitionParser();
18+
}
19+
}

tests/Provider/ColumnDefinitionParserProvider.php

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,30 @@ public static function parse(): array
2828
['int DEFAULT (1 + 2)', ['type' => 'int', 'defaultValueRaw' => '(1 + 2)']],
2929
["int COMMENT '''Quoted'' comment'", ['type' => 'int', 'comment' => "'Quoted' comment"]],
3030
['int CHECK (value > (1 + 5))', ['type' => 'int', 'check' => 'value > (1 + 5)']],
31-
["enum('a','b','c')", ['type' => 'enum', 'enumValues' => ['a', 'b', 'c']]],
32-
["enum('a','b','c') NOT NULL", ['type' => 'enum', 'enumValues' => ['a', 'b', 'c'], 'notNull' => true]],
31+
[
32+
"enum('a','b','c')",
33+
['type' => 'enum', 'enumValues' => ['a', 'b', 'c']],
34+
],
35+
[
36+
"enum('a','b','c') NOT NULL",
37+
['type' => 'enum', 'enumValues' => ['a', 'b', 'c'], 'notNull' => true],
38+
],
39+
'enum-with-square-brackets' => [
40+
"enum('[one]', 'the [two]', 'the [three] to') NOT NULL",
41+
['type' => 'enum', 'enumValues' => ['[one]', 'the [two]', 'the [three] to'], 'notNull' => true],
42+
],
43+
'enum-with-parentheses' => [
44+
"enum('(one)', 'the (two)', 'the (three) to') NOT NULL",
45+
['type' => 'enum', 'enumValues' => ['(one)', 'the (two)', 'the (three) to'], 'notNull' => true],
46+
],
47+
'enum-with-escaped-quotes' => [
48+
"enum('hello''world''', 'the ''[feature]''') NOT NULL",
49+
['type' => 'enum', 'enumValues' => ["hello'world'", "the '[feature]'"], 'notNull' => true],
50+
],
51+
'enum-with-parentheses-and-escaped-quotes' => [
52+
"enum('''hey (one)', 'the (t''wo)', 'the (three) ''to') NOT NULL",
53+
['type' => 'enum', 'enumValues' => ['\'hey (one)', 'the (t\'wo)', 'the (three) \'to'], 'notNull' => true],
54+
],
3355
['int[]', ['type' => 'int', 'dimension' => 1]],
3456
['string(126)[][]', ['type' => 'string', 'size' => 126, 'dimension' => 2]],
3557
];

0 commit comments

Comments
 (0)