From 0e7e77dd7544fbcb5b58a29be3fe6f622ede792b Mon Sep 17 00:00:00 2001 From: Tigrov Date: Sun, 17 Nov 2024 10:55:17 +0700 Subject: [PATCH] Improve `ColumnDefinitionParser::extraInfo()` method --- src/Syntax/ColumnDefinitionParser.php | 49 +++++++++++++++++-- tests/AbstractColumnFactoryTest.php | 4 +- .../ColumnDefinitionParserProvider.php | 17 +++++-- tests/Provider/ColumnFactoryProvider.php | 2 +- 4 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/Syntax/ColumnDefinitionParser.php b/src/Syntax/ColumnDefinitionParser.php index cc7329b87..ebc0b40f4 100644 --- a/src/Syntax/ColumnDefinitionParser.php +++ b/src/Syntax/ColumnDefinitionParser.php @@ -7,8 +7,7 @@ use function explode; use function preg_match; use function preg_match_all; -use function str_ireplace; -use function stripos; +use function str_replace; use function strlen; use function strtolower; use function substr; @@ -27,11 +26,15 @@ final class ColumnDefinitionParser * @return array The column information. * * @psalm-return array{ + * check?: string, + * defaultValueRaw?: string, * enumValues?: list, * extra?: string, + * notNull?: bool, * scale?: int, * size?: int, * type: lowercase-string, + * unique?: bool, * unsigned?: bool, * } */ @@ -66,7 +69,14 @@ private function enumInfo(string $values): array } /** - * @psalm-return array{unsigned?: bool, extra?: string} + * @psalm-return array{ + * check?: string, + * defaultValueRaw?: string, + * extra?: string, + * notNull?: bool, + * unique?: bool, + * unsigned?: bool + * } */ private function extraInfo(string $extra): array { @@ -75,12 +85,41 @@ private function extraInfo(string $extra): array } $info = []; + $bracketsPattern = '(\(((?>[^()]+)|(?-2))*\))'; + $defaultPattern = "/\\s*\\bDEFAULT\\s+('(?:[^']|'')*'|\"(?:[^\"]|\"\")*\"|[^(\\s]*$bracketsPattern?\\S*)/i"; + + if (preg_match($defaultPattern, $extra, $matches) === 1) { + $info['defaultValueRaw'] = $matches[1]; + $extra = str_replace($matches[0], '', $extra); + } + + if (preg_match("/\\s*\\bCHECK\\s+$bracketsPattern/i", $extra, $matches) === 1) { + $info['check'] = substr($matches[1], 1, -1); + $extra = str_replace($matches[0], '', $extra); + } - if (stripos($extra, 'unsigned') !== false) { + $extra = preg_replace('/\s*\bUNSIGNED\b/i', '', $extra, 1, $count); + if ($count > 0) { $info['unsigned'] = true; - $extra = trim(str_ireplace('unsigned', '', $extra)); } + $extra = preg_replace('/\s*\bUNIQUE\b/i', '', $extra, 1, $count); + if ($count > 0) { + $info['unique'] = true; + } + + $extra = preg_replace('/\s*\bNOT\s+NULL\b/i', '', $extra, 1, $count); + if ($count > 0) { + $info['notNull'] = true; + } else { + $extra = preg_replace('/\s*\bNULL\b/i', '', $extra, 1, $count); + if ($count > 0) { + $info['notNull'] = false; + } + } + + $extra = trim($extra); + if (!empty($extra)) { $info['extra'] = $extra; } diff --git a/tests/AbstractColumnFactoryTest.php b/tests/AbstractColumnFactoryTest.php index 1393658b5..53489c0ca 100644 --- a/tests/AbstractColumnFactoryTest.php +++ b/tests/AbstractColumnFactoryTest.php @@ -104,12 +104,12 @@ public function testFromDefinitionWithExtra(): void $db = $this->getConnection(); $columnFactory = $db->getSchema()->getColumnFactory(); - $column = $columnFactory->fromDefinition('char(1) NOT NULL', ['extra' => 'UNIQUE']); + $column = $columnFactory->fromDefinition('char(1) INVISIBLE', ['extra' => 'COLLATE utf8mb4']); $this->assertInstanceOf(StringColumnSchema::class, $column); $this->assertSame('char', $column->getType()); $this->assertSame(1, $column->getSize()); - $this->assertSame('NOT NULL UNIQUE', $column->getExtra()); + $this->assertSame('INVISIBLE COLLATE utf8mb4', $column->getExtra()); $db->close(); } diff --git a/tests/Provider/ColumnDefinitionParserProvider.php b/tests/Provider/ColumnDefinitionParserProvider.php index fe7b2eede..91816f253 100644 --- a/tests/Provider/ColumnDefinitionParserProvider.php +++ b/tests/Provider/ColumnDefinitionParserProvider.php @@ -13,12 +13,21 @@ public static function parse(): array ['int', ['type' => 'int']], ['int(10)', ['type' => 'int', 'size' => 10]], ['int UNSIGNED', ['type' => 'int', 'unsigned' => true]], + ['int UNIQUE', ['type' => 'int', 'unique' => true]], ['int(10) UNSIGNED', ['type' => 'int', 'size' => 10, 'unsigned' => true]], - ['int(10) UNSIGNED NOT NULL', ['type' => 'int', 'size' => 10, 'unsigned' => true, 'extra' => 'NOT NULL']], - ['int(10) NOT NULL', ['type' => 'int', 'size' => 10, 'extra' => 'NOT NULL']], - ['text NOT NULL', ['type' => 'text', 'extra' => 'NOT NULL']], + ['int(10) UNSIGNED NOT NULL', ['type' => 'int', 'size' => 10, 'unsigned' => true, 'notNull' => true]], + ['int(10) NOT NULL', ['type' => 'int', 'size' => 10, 'notNull' => true]], + ['text NOT NULL', ['type' => 'text', 'notNull' => true]], + ['text NULL', ['type' => 'text', 'notNull' => false]], + ['text COLLATE utf8mb4', ['type' => 'text', 'extra' => 'COLLATE utf8mb4']], + ['text DEFAULT NULL', ['type' => 'text', 'defaultValueRaw' => 'NULL']], + ["text DEFAULT 'value'", ['type' => 'text', 'defaultValueRaw' => "'value'"]], + ['varchar(36) DEFAULT uuid()', ['type' => 'varchar', 'size' => 36, 'defaultValueRaw' => 'uuid()']], + ['varchar(36) DEFAULT uuid()::varchar(36)', ['type' => 'varchar', 'size' => 36, 'defaultValueRaw' => 'uuid()::varchar(36)']], + ['int DEFAULT (1 + 2)', ['type' => 'int', 'defaultValueRaw' => '(1 + 2)']], + ['int CHECK (value > (1 + 5))', ['type' => 'int', 'check' => 'value > (1 + 5)']], ["enum('a','b','c')", ['type' => 'enum', 'enumValues' => ['a', 'b', 'c']]], - ["enum('a','b','c') NOT NULL", ['type' => 'enum', 'enumValues' => ['a', 'b', 'c'], 'extra' => 'NOT NULL']], + ["enum('a','b','c') NOT NULL", ['type' => 'enum', 'enumValues' => ['a', 'b', 'c'], 'notNull' => true]], ]; } } diff --git a/tests/Provider/ColumnFactoryProvider.php b/tests/Provider/ColumnFactoryProvider.php index 1b05828b2..71bc2f18d 100644 --- a/tests/Provider/ColumnFactoryProvider.php +++ b/tests/Provider/ColumnFactoryProvider.php @@ -24,7 +24,7 @@ public static function definitions(): array // definition, expected type, expected instance of, expected column method results '' => ['', ColumnType::STRING, StringColumnSchema::class, ['getDbType' => '']], 'text' => ['text', ColumnType::TEXT, StringColumnSchema::class, ['getDbType' => 'text']], - 'text NOT NULL' => ['text NOT NULL', ColumnType::TEXT, StringColumnSchema::class, ['getDbType' => 'text', 'getExtra' => 'NOT NULL']], + 'text NOT NULL' => ['text NOT NULL', ColumnType::TEXT, StringColumnSchema::class, ['getDbType' => 'text', 'isNotNull' => true]], 'char(1)' => ['char(1)', ColumnType::CHAR, StringColumnSchema::class, ['getDbType' => 'char', 'getSize' => 1]], 'decimal(10,2)' => ['decimal(10,2)', ColumnType::DECIMAL, DoubleColumnSchema::class, ['getDbType' => 'decimal', 'getSize' => 10, 'getScale' => 2]], 'bigint UNSIGNED' => ['bigint UNSIGNED', ColumnType::BIGINT, BigIntColumnSchema::class, ['getDbType' => 'bigint', 'isUnsigned' => true]],