From 1377e80b0b00df0d2b4ef0cc2a16bab8a8ffe6cc Mon Sep 17 00:00:00 2001 From: Lukas Rydygel Date: Thu, 1 Oct 2026 09:24:21 +0200 Subject: [PATCH 1/3] Fix ColumnDefinitionParser to parse unquoted DEFAULT NULL as SQL NULL String defaults are always quoted in a column definition, so an unquoted NULL can only be SQL NULL. Previously it was stored as defaultValueRaw 'NULL' and the MySQL column factory typecast it to 0 for integer columns (e.g. 'int DEFAULT NULL' was created as DEFAULT 0). See yiisoft/db-mysql#485 --- CHANGELOG.md | 2 ++ src/Syntax/ColumnDefinitionParser.php | 10 ++++++++-- tests/Provider/ColumnDefinitionParserProvider.php | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d182acd0b..634808f00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ - Enh #1201: Make `$psrCache` parameter of `SchemaCache` constructor and `$schemaCache` parameter of `AbstractSchema` and `AbstractPdoConnection` constructors optional, remove `psr/simple-cache-implementation` from `require` in `composer.json` (@vjik) +- Bug #1202: Fix `ColumnDefinitionParser` to parse an unquoted `DEFAULT NULL` as SQL `NULL` instead of the string + `'NULL'`, which MySQL typecast to `DEFAULT 0` for integer columns (@Lugat) ## 2.0.1 February 09, 2026 diff --git a/src/Syntax/ColumnDefinitionParser.php b/src/Syntax/ColumnDefinitionParser.php index 0bd557f74..686d9ad2e 100644 --- a/src/Syntax/ColumnDefinitionParser.php +++ b/src/Syntax/ColumnDefinitionParser.php @@ -9,6 +9,7 @@ use function preg_match_all; use function preg_replace; use function str_replace; +use function strcasecmp; use function strlen; use function strtolower; use function substr; @@ -22,7 +23,7 @@ * check?: string, * collation?: string, * comment?: string, - * defaultValueRaw?: string, + * defaultValueRaw?: string|null, * extra?: string, * notNull?: bool, * unique?: bool, @@ -42,7 +43,7 @@ class ColumnDefinitionParser * check?: string, * collation?: string, * comment?: string, - * defaultValueRaw?: string, + * defaultValueRaw?: string|null, * dimension?: positive-int, * extra?: string, * notNull?: bool, @@ -115,6 +116,11 @@ protected function extraInfo(string $extra): array $extra = $this->parseBoolValue($extra, '/\s*\bUNIQUE\b/i', 'unique', $info); $extra = $this->parseBoolValue($extra, '/\s*\bNOT\s+NULL\b/i', 'notNull', $info); + // String defaults are always quoted in a column definition, so an unquoted `NULL` can only be SQL `NULL` + if (isset($info['defaultValueRaw']) && strcasecmp($info['defaultValueRaw'], 'NULL') === 0) { + $info['defaultValueRaw'] = null; + } + if (empty($info['notNull'])) { $extra = $this->parseBoolValue($extra, '/\s*\bNULL\b/i', 'notNull', $info); diff --git a/tests/Provider/ColumnDefinitionParserProvider.php b/tests/Provider/ColumnDefinitionParserProvider.php index 07d0eb781..275b06184 100644 --- a/tests/Provider/ColumnDefinitionParserProvider.php +++ b/tests/Provider/ColumnDefinitionParserProvider.php @@ -21,7 +21,11 @@ public static function parse(): array ['text NULL', ['type' => 'text', 'notNull' => false]], ['text COLLATE utf8mb4', ['type' => 'text', 'collation' => 'utf8mb4']], ["text COMPRESSION 'LZ4'", ['type' => 'text', 'extra' => "COMPRESSION 'LZ4'"]], - ['text DEFAULT NULL', ['type' => 'text', 'defaultValueRaw' => 'NULL']], + ['text DEFAULT NULL', ['type' => 'text', 'defaultValueRaw' => null]], + ['int default null', ['type' => 'int', 'defaultValueRaw' => null]], + ['int NULL DEFAULT NULL', ['type' => 'int', 'defaultValueRaw' => null, 'notNull' => false]], + ['int DEFAULT 5', ['type' => 'int', 'defaultValueRaw' => '5']], + ["varchar(10) DEFAULT 'NULL'", ['type' => 'varchar', 'size' => 10, '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)']], From f1ccc5f5273f6e0af30bba98a12f80cb20d36721 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 1 Oct 2026 12:20:54 +0300 Subject: [PATCH 2/3] fix psalm --- src/Syntax/ColumnDefinitionParser.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Syntax/ColumnDefinitionParser.php b/src/Syntax/ColumnDefinitionParser.php index 686d9ad2e..129d3ef2a 100644 --- a/src/Syntax/ColumnDefinitionParser.php +++ b/src/Syntax/ColumnDefinitionParser.php @@ -117,6 +117,7 @@ protected function extraInfo(string $extra): array $extra = $this->parseBoolValue($extra, '/\s*\bNOT\s+NULL\b/i', 'notNull', $info); // String defaults are always quoted in a column definition, so an unquoted `NULL` can only be SQL `NULL` + /** @psalm-var ExtraInfo $info */ if (isset($info['defaultValueRaw']) && strcasecmp($info['defaultValueRaw'], 'NULL') === 0) { $info['defaultValueRaw'] = null; } From fc4fecea92ba9e090f2ba0879202e26ac6bfee78 Mon Sep 17 00:00:00 2001 From: Sergei Tigrov Date: Tue, 6 Oct 2026 09:04:33 +0700 Subject: [PATCH 3/3] Apply batched suggestions from code review Co-authored-by: Sergei Predvoditelev Co-authored-by: Sergei Tigrov --- CHANGELOG.md | 2 +- src/Syntax/ColumnDefinitionParser.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 634808f00..aab41b47c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ `AbstractSchema` and `AbstractPdoConnection` constructors optional, remove `psr/simple-cache-implementation` from `require` in `composer.json` (@vjik) - Bug #1202: Fix `ColumnDefinitionParser` to parse an unquoted `DEFAULT NULL` as SQL `NULL` instead of the string - `'NULL'`, which MySQL typecast to `DEFAULT 0` for integer columns (@Lugat) + `'NULL'` (@Lugat) ## 2.0.1 February 09, 2026 diff --git a/src/Syntax/ColumnDefinitionParser.php b/src/Syntax/ColumnDefinitionParser.php index 129d3ef2a..528f89af5 100644 --- a/src/Syntax/ColumnDefinitionParser.php +++ b/src/Syntax/ColumnDefinitionParser.php @@ -116,7 +116,6 @@ protected function extraInfo(string $extra): array $extra = $this->parseBoolValue($extra, '/\s*\bUNIQUE\b/i', 'unique', $info); $extra = $this->parseBoolValue($extra, '/\s*\bNOT\s+NULL\b/i', 'notNull', $info); - // String defaults are always quoted in a column definition, so an unquoted `NULL` can only be SQL `NULL` /** @psalm-var ExtraInfo $info */ if (isset($info['defaultValueRaw']) && strcasecmp($info['defaultValueRaw'], 'NULL') === 0) { $info['defaultValueRaw'] = null;