From 6153928980edcf5f2569b18043f447f4796ce332 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 10 Jul 2025 06:51:40 +0000 Subject: [PATCH 01/10] style(php-cs-fixer): fix coding standards --- src/Driver/Jsoner.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Driver/Jsoner.php b/src/Driver/Jsoner.php index 7bc50eaa..fe08c02c 100644 --- a/src/Driver/Jsoner.php +++ b/src/Driver/Jsoner.php @@ -32,7 +32,7 @@ public static function toJson(mixed $value, bool $encode = true, bool $validate $result = (string) $value; - if ($validate && !json_validate($result)) { + if ($validate && !\json_validate($result)) { throw new BuilderException('Invalid JSON value.'); } From a8c32925d42997c49bdf376a824b5d67786e415b Mon Sep 17 00:00:00 2001 From: Adam Dyson Date: Thu, 10 Jul 2025 16:52:32 +1000 Subject: [PATCH 02/10] Ensures differences in size for boolean columns are ignore when matching defaults --- src/Driver/Jsoner.php | 2 +- src/Driver/MySQL/Schema/MySQLColumn.php | 4 +++ .../Driver/MySQL/Schema/BooleanColumnTest.php | 29 +++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/Driver/Jsoner.php b/src/Driver/Jsoner.php index 7bc50eaa..fe08c02c 100644 --- a/src/Driver/Jsoner.php +++ b/src/Driver/Jsoner.php @@ -32,7 +32,7 @@ public static function toJson(mixed $value, bool $encode = true, bool $validate $result = (string) $value; - if ($validate && !json_validate($result)) { + if ($validate && !\json_validate($result)) { throw new BuilderException('Invalid JSON value.'); } diff --git a/src/Driver/MySQL/Schema/MySQLColumn.php b/src/Driver/MySQL/Schema/MySQLColumn.php index c8cf6464..11293307 100644 --- a/src/Driver/MySQL/Schema/MySQLColumn.php +++ b/src/Driver/MySQL/Schema/MySQLColumn.php @@ -332,6 +332,10 @@ public function compare(AbstractColumn $initial): bool return $result && $this->size === $initial->size; } + if (! $result && $this->userType === 'boolean' && $this->size === 1 && $initial->size === 4) { + return true; // Ignore size differences for boolean columns when matching defaults + } + return $result; } diff --git a/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php b/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php index c999ddf3..f8d3d7b3 100644 --- a/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php +++ b/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php @@ -5,6 +5,7 @@ namespace Cycle\Database\Tests\Functional\Driver\MySQL\Schema; // phpcs:ignore +use Cycle\Database\Driver\MySQL\Schema\MySQLColumn; use Cycle\Database\Tests\Functional\Driver\Common\Schema\BooleanColumnTest as CommonClass; /** @@ -14,4 +15,32 @@ class BooleanColumnTest extends CommonClass { public const DRIVER = 'mysql'; + + public function testBooleanDefaultSize(): void + { + $schema = $this->schema('table'); + $schema->boolean('column'); + $schema->save(); + + $column = $this->fetchSchema($schema)->column('column'); + + $this->assertSame('boolean', $column->getAbstractType()); + $this->assertSame(1, $column->getSize()); + } + + public function testBooleanComparisonWithSize(): void + { + $schema = $this->schema('table'); + $this->assertFalse($schema->exists()); + + /** @var MySQLColumn $column */ + $column = $schema->boolean('column')->nullable(false)->unsigned(true); + + $schema->save(); + $schema = $this->schema('table'); + $this->assertTrue($schema->exists()); + $this->assertSame(1, $column->getSize()); + $this->assertSame(4, $schema->column('column')->getSize()); + $this->assertTrue($schema->column('column')->compare($column)); + } } From 9c1911f9d5098655ec8d433ca81d2e30b42c27af Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Thu, 10 Jul 2025 11:12:12 +0400 Subject: [PATCH 03/10] chore: test without hostfix --- src/Driver/MySQL/Schema/MySQLColumn.php | 6 +++--- .../Functional/Driver/MySQL/Schema/BooleanColumnTest.php | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Driver/MySQL/Schema/MySQLColumn.php b/src/Driver/MySQL/Schema/MySQLColumn.php index 11293307..58b7d557 100644 --- a/src/Driver/MySQL/Schema/MySQLColumn.php +++ b/src/Driver/MySQL/Schema/MySQLColumn.php @@ -332,9 +332,9 @@ public function compare(AbstractColumn $initial): bool return $result && $this->size === $initial->size; } - if (! $result && $this->userType === 'boolean' && $this->size === 1 && $initial->size === 4) { - return true; // Ignore size differences for boolean columns when matching defaults - } + // if (! $result && $this->userType === 'boolean' && $this->size === 1 && $initial->size === 4) { + // return true; // Ignore size differences for boolean columns when matching defaults + // } return $result; } diff --git a/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php b/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php index f8d3d7b3..ffa83820 100644 --- a/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php +++ b/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php @@ -42,5 +42,6 @@ public function testBooleanComparisonWithSize(): void $this->assertSame(1, $column->getSize()); $this->assertSame(4, $schema->column('column')->getSize()); $this->assertTrue($schema->column('column')->compare($column)); + $this->assertTrue($column->compare($schema->column('column'))); } } From 0748d37e045554b2fe6ce794c15900379fbcbcba Mon Sep 17 00:00:00 2001 From: Adam Dyson Date: Thu, 10 Jul 2025 23:54:56 +1000 Subject: [PATCH 04/10] Added a test case which proves the issue --- .../Driver/MySQL/Schema/BooleanColumnTest.php | 56 +++++++++++++++++-- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php b/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php index f8d3d7b3..cecd56f1 100644 --- a/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php +++ b/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php @@ -31,16 +31,60 @@ public function testBooleanDefaultSize(): void public function testBooleanComparisonWithSize(): void { $schema = $this->schema('table'); - $this->assertFalse($schema->exists()); + $foo = $schema->boolean('foo')->defaultValue(false)->nullable(false)->unsigned(true)->size(1)->zerofill(true); + $bar = $schema->boolean('bar', nullable: true, unsigned: true, size: 1, zerofill: true); + $baz = $schema->boolean('baz', nullable: false, unsigned: true); + $mux = $schema->boolean('mux', nullable: false); + $schema->save(); - /** @var MySQLColumn $column */ - $column = $schema->boolean('column')->nullable(false)->unsigned(true); + $schema = $this->schema('table'); + $this->assertTrue($schema->exists()); + $this->assertSame(1, $foo->getSize()); + $this->assertSame(1, $bar->getSize()); + $this->assertSame(1, $baz->getSize()); + $this->assertSame(1, $mux->getSize()); + $this->assertSame(1, $schema->column('foo')->getSize()); + $this->assertSame(1, $schema->column('bar')->getSize()); + $this->assertTrue(\in_array($schema->column('baz')->getSize(), [1, 4], true)); + $this->assertSame(1, $schema->column('mux')->getSize()); + $this->assertTrue($foo->compare($schema->column('foo'))); + $this->assertTrue($bar->compare($schema->column('bar'))); + $this->assertTrue($baz->compare($schema->column('baz'))); + $this->assertTrue($mux->compare($schema->column('mux'))); + } - $schema->save(); + public function testBooleanWithProblematicValues(): void + { $schema = $this->schema('table'); + + $column = $schema->boolean('target') + ->defaultValue(false) + ->nullable(false) + ->unsigned(true) + ->comment('Target comment'); + + $schema->save(); + $this->assertTrue($schema->exists()); + + $schema = $this->schema('table'); + $target = $schema->column('target'); + $this->assertSame(1, $column->getSize()); - $this->assertSame(4, $schema->column('column')->getSize()); - $this->assertTrue($schema->column('column')->compare($column)); + $this->assertSame(4, $target->getSize()); + $this->assertFalse($column->isNullable()); + $this->assertFalse($target->isNullable()); + $this->assertTrue($column->isUnsigned()); + $this->assertTrue($target->isUnsigned()); + + $object = new \ReflectionObject($target); + $property = $object->getProperty('defaultValue'); + $property->setAccessible(true); + $defaultValue = $property->getValue($target); + + $this->assertSame(false, $column->getDefaultValue()); + $this->assertSame(0, $target->getDefaultValue()); + $this->assertSame('0', $defaultValue); + $this->assertTrue($column->compare($target)); } } From d7811b86269dea9b2a597c2a392cb963b485c0d4 Mon Sep 17 00:00:00 2001 From: Adam Dyson Date: Thu, 10 Jul 2025 23:56:21 +1000 Subject: [PATCH 05/10] Added a test case which confirms the issue --- .../Functional/Driver/MySQL/Schema/BooleanColumnTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php b/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php index cecd56f1..23923327 100644 --- a/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php +++ b/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php @@ -5,7 +5,6 @@ namespace Cycle\Database\Tests\Functional\Driver\MySQL\Schema; // phpcs:ignore -use Cycle\Database\Driver\MySQL\Schema\MySQLColumn; use Cycle\Database\Tests\Functional\Driver\Common\Schema\BooleanColumnTest as CommonClass; /** From c53289b18b66c478a8a78a645cfa961d440360eb Mon Sep 17 00:00:00 2001 From: Adam Dyson Date: Fri, 11 Jul 2025 00:03:59 +1000 Subject: [PATCH 06/10] Removed code which should be here --- src/Driver/MySQL/Schema/MySQLColumn.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Driver/MySQL/Schema/MySQLColumn.php b/src/Driver/MySQL/Schema/MySQLColumn.php index 58b7d557..c8cf6464 100644 --- a/src/Driver/MySQL/Schema/MySQLColumn.php +++ b/src/Driver/MySQL/Schema/MySQLColumn.php @@ -332,10 +332,6 @@ public function compare(AbstractColumn $initial): bool return $result && $this->size === $initial->size; } - // if (! $result && $this->userType === 'boolean' && $this->size === 1 && $initial->size === 4) { - // return true; // Ignore size differences for boolean columns when matching defaults - // } - return $result; } From 67ec4381201767e2f66f95878518b3564f93e870 Mon Sep 17 00:00:00 2001 From: Adam Dyson Date: Fri, 11 Jul 2025 00:10:31 +1000 Subject: [PATCH 07/10] Fixes the issue when comparing boolean columns under certain circumstances --- src/Schema/AbstractColumn.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Schema/AbstractColumn.php b/src/Schema/AbstractColumn.php index f83ceb79..8fd1b524 100644 --- a/src/Schema/AbstractColumn.php +++ b/src/Schema/AbstractColumn.php @@ -597,6 +597,13 @@ public function compare(self $initial): bool ? $initial->getDefaultValue()->__toString() : $initial->getDefaultValue(); + $defaultValue = $this->userType === 'boolean' + ? (bool) $defaultValue + : $defaultValue; + $initialDefaultValue = $this->userType === 'boolean' + ? (bool) $initialDefaultValue + : $initialDefaultValue; + //Default values has to compared using type-casted value if ($defaultValue != $initialDefaultValue) { $difference[] = $name; From f47cdbfd47e4fa50cfd390bc8dd4e7cfef57b7b6 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Mon, 14 Jul 2025 12:40:55 +0400 Subject: [PATCH 08/10] refactor: simplify boolean value handling in default value assignment --- src/Schema/AbstractColumn.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Schema/AbstractColumn.php b/src/Schema/AbstractColumn.php index 15e2b777..c34ab6c5 100644 --- a/src/Schema/AbstractColumn.php +++ b/src/Schema/AbstractColumn.php @@ -598,12 +598,10 @@ public function compare(self $initial): bool ? $initial->getDefaultValue()->__toString() : $initial->getDefaultValue(); - $defaultValue = $this->userType === 'boolean' - ? (bool) $defaultValue - : $defaultValue; - $initialDefaultValue = $this->userType === 'boolean' - ? (bool) $initialDefaultValue - : $initialDefaultValue; + if ($this->userType === 'boolean') { + $defaultValue = (bool) $defaultValue; + $initialDefaultValue = (bool) $initialDefaultValue; + } //Default values has to compared using type-casted value if ($defaultValue != $initialDefaultValue) { From e03bc4ea4d3799f21d80f9ea23ad69a5a9380797 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Mon, 14 Jul 2025 13:52:50 +0400 Subject: [PATCH 09/10] fix: enhance boolean column comparison handling for unknown sizes --- src/Driver/MySQL/Schema/MySQLColumn.php | 35 +++++++++++++++++-- src/Schema/AbstractColumn.php | 5 --- .../Driver/MySQL/Schema/BooleanColumnTest.php | 1 + 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/Driver/MySQL/Schema/MySQLColumn.php b/src/Driver/MySQL/Schema/MySQLColumn.php index ad823afa..e8e3a40f 100644 --- a/src/Driver/MySQL/Schema/MySQLColumn.php +++ b/src/Driver/MySQL/Schema/MySQLColumn.php @@ -162,6 +162,11 @@ class MySQLColumn extends AbstractColumn )] protected int $size = 0; + /** + * True if size is not defined in DB schema. + */ + protected bool $unknownSize = false; + /** * Column is auto incremental. */ @@ -251,6 +256,7 @@ public static function createInstance(string $table, array $schema, ?\DateTimeZo // since 8.0 database does not provide size for some columns if ($column->size === 0) { + $column->unknownSize = true; switch ($column->type) { case 'int': $column->size = 11; @@ -291,6 +297,12 @@ public static function createInstance(string $table, array $schema, ?\DateTimeZo return $column; } + public function size(int $value): self + { + $this->unknownSize = false; + return parent::__call('size', [$value]); + } + /** * @psalm-return non-empty-string */ @@ -327,10 +339,27 @@ public function sqlStatement(DriverInterface $driver): string public function compare(AbstractColumn $initial): bool { - $result = parent::compare($initial); + \assert($initial instanceof self); + $self = $this; + + // MySQL 8.0 does not provide size for unsigned integers without zerofill + // so we can get wrong results in comparison of boolean columns + if ($self->unknownSize || $initial->unknownSize) { + // if one of the columns is boolean, we can safely assume that size is 1 + if (\in_array($self->userType, ['bool', 'boolean'], true)) { + $initial = clone $initial; + $initial->size = 1; + } elseif (\in_array($initial->userType, ['bool', 'boolean'], true)) { + $self = clone $self; + $self->size = 1; + } + } + + $result = \Closure::fromCallable([parent::class, 'compare'])->bindTo($self)($initial); + - if ($this->type === 'varchar' || $this->type === 'varbinary') { - return $result && $this->size === $initial->size; + if ($self->type === 'varchar' || $self->type === 'varbinary') { + return $result && $self->size === $initial->size; } return $result; diff --git a/src/Schema/AbstractColumn.php b/src/Schema/AbstractColumn.php index c34ab6c5..283d29ad 100644 --- a/src/Schema/AbstractColumn.php +++ b/src/Schema/AbstractColumn.php @@ -598,11 +598,6 @@ public function compare(self $initial): bool ? $initial->getDefaultValue()->__toString() : $initial->getDefaultValue(); - if ($this->userType === 'boolean') { - $defaultValue = (bool) $defaultValue; - $initialDefaultValue = (bool) $initialDefaultValue; - } - //Default values has to compared using type-casted value if ($defaultValue != $initialDefaultValue) { $difference[] = $name; diff --git a/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php b/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php index 23923327..e5bf5eb5 100644 --- a/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php +++ b/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php @@ -85,5 +85,6 @@ public function testBooleanWithProblematicValues(): void $this->assertSame(0, $target->getDefaultValue()); $this->assertSame('0', $defaultValue); $this->assertTrue($column->compare($target)); + $this->assertTrue($target->compare($column)); } } From 28128ac6ccb654d068c6bc98c48ff4acbebbf361 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Mon, 14 Jul 2025 14:06:24 +0400 Subject: [PATCH 10/10] fix: update boolean column comparison to exclude unknown size from checks --- src/Driver/MySQL/Schema/MySQLColumn.php | 2 +- .../Functional/Driver/MySQL/Schema/BooleanColumnTest.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Driver/MySQL/Schema/MySQLColumn.php b/src/Driver/MySQL/Schema/MySQLColumn.php index e8e3a40f..28ee7321 100644 --- a/src/Driver/MySQL/Schema/MySQLColumn.php +++ b/src/Driver/MySQL/Schema/MySQLColumn.php @@ -42,7 +42,7 @@ class MySQLColumn extends AbstractColumn */ public const DATETIME_NOW = 'CURRENT_TIMESTAMP'; - public const EXCLUDE_FROM_COMPARE = ['size', 'timezone', 'userType', 'attributes', 'first', 'after']; + public const EXCLUDE_FROM_COMPARE = ['size', 'timezone', 'userType', 'attributes', 'first', 'after', 'unknownSize']; protected const INTEGER_TYPES = ['tinyint', 'smallint', 'mediumint', 'int', 'bigint']; protected array $mapping = [ diff --git a/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php b/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php index e5bf5eb5..8eeb7096 100644 --- a/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php +++ b/tests/Database/Functional/Driver/MySQL/Schema/BooleanColumnTest.php @@ -86,5 +86,7 @@ public function testBooleanWithProblematicValues(): void $this->assertSame('0', $defaultValue); $this->assertTrue($column->compare($target)); $this->assertTrue($target->compare($column)); + // The size was not changed + $this->assertSame(4, $target->getSize()); } }