diff --git a/src/Db/Adapter/MysqlAdapter.php b/src/Db/Adapter/MysqlAdapter.php index 0873581a..77626858 100644 --- a/src/Db/Adapter/MysqlAdapter.php +++ b/src/Db/Adapter/MysqlAdapter.php @@ -633,6 +633,9 @@ public function getColumns(string $tableName): array if ($record['onUpdate'] ?? false) { $column->setUpdate($record['onUpdate']); } + if ($record['fixed'] ?? false) { + $column->setFixed(true); + } $columns[] = $column; } diff --git a/src/Db/Table/Column.php b/src/Db/Table/Column.php index 98f28b31..e608560e 100644 --- a/src/Db/Table/Column.php +++ b/src/Db/Table/Column.php @@ -117,6 +117,11 @@ class Column extends DatabaseColumn */ protected ?string $lock = null; + /** + * @var bool|null + */ + protected ?bool $fixed = null; + /** * Column constructor * @@ -772,6 +777,31 @@ public function getLock(): ?string return $this->lock; } + /** + * Sets whether field should use fixed-length storage (for binary columns). + * + * When true, binary columns will use BINARY(n) instead of VARBINARY(n). + * + * @param bool $fixed Fixed + * @return $this + */ + public function setFixed(bool $fixed) + { + $this->fixed = $fixed; + + return $this; + } + + /** + * Gets whether field should use fixed-length storage. + * + * @return bool|null + */ + public function getFixed(): ?bool + { + return $this->fixed; + } + /** * Gets all allowed options. Each option must have a corresponding `setFoo` method. * @@ -802,6 +832,7 @@ protected function getValidOptions(): array 'generated', 'algorithm', 'lock', + 'fixed', ]; } @@ -894,6 +925,7 @@ public function toArray(): array 'default' => $default, 'generated' => $this->getGenerated(), 'unsigned' => $this->getUnsigned(), + 'fixed' => $this->getFixed(), 'onUpdate' => $this->getUpdate(), 'collate' => $this->getCollation(), 'precision' => $precision, diff --git a/src/View/Helper/MigrationHelper.php b/src/View/Helper/MigrationHelper.php index 3302e7e5..75e04cbb 100644 --- a/src/View/Helper/MigrationHelper.php +++ b/src/View/Helper/MigrationHelper.php @@ -389,6 +389,7 @@ public function getColumnOption(array $options): array 'scale', 'after', 'collate', + 'fixed', ]); $columnOptions = array_intersect_key($options, $wantedOptions); if (empty($columnOptions['comment'])) { @@ -495,7 +496,7 @@ public function attributes(TableSchemaInterface|string $table, string $column): 'comment', 'unsigned', 'signed', 'properties', 'autoIncrement', 'unique', - 'collate', + 'collate', 'fixed', ]; $attributes = []; diff --git a/tests/TestCase/Db/Adapter/MysqlAdapterTest.php b/tests/TestCase/Db/Adapter/MysqlAdapterTest.php index 9687db82..8d171c71 100644 --- a/tests/TestCase/Db/Adapter/MysqlAdapterTest.php +++ b/tests/TestCase/Db/Adapter/MysqlAdapterTest.php @@ -3484,4 +3484,53 @@ public function testCombinedPartitionAndColumnOperations(): void $this->assertCount(1, $rows); $this->assertEquals('A description', $rows[0]['description']); } + + public function testBinaryColumnWithFixedOption(): void + { + $table = new Table('binary_fixed_test', [], $this->adapter); + $table->addColumn('hash', 'binary', ['limit' => 20, 'fixed' => true]) + ->addColumn('data', 'binary', ['limit' => 20]) + ->save(); + + $this->assertTrue($this->adapter->hasColumn('binary_fixed_test', 'hash')); + $this->assertTrue($this->adapter->hasColumn('binary_fixed_test', 'data')); + + // Check that the fixed column is created as BINARY and the non-fixed as VARBINARY + $rows = $this->adapter->fetchAll('SHOW COLUMNS FROM binary_fixed_test'); + $hashColumn = null; + $dataColumn = null; + foreach ($rows as $row) { + if ($row['Field'] === 'hash') { + $hashColumn = $row; + } + if ($row['Field'] === 'data') { + $dataColumn = $row; + } + } + + $this->assertNotNull($hashColumn); + $this->assertNotNull($dataColumn); + $this->assertSame('binary(20)', $hashColumn['Type']); + $this->assertSame('varbinary(20)', $dataColumn['Type']); + + // Verify the fixed attribute is reflected back + $columns = $this->adapter->getColumns('binary_fixed_test'); + $hashCol = null; + $dataCol = null; + foreach ($columns as $col) { + if ($col->getName() === 'hash') { + $hashCol = $col; + } + if ($col->getName() === 'data') { + $dataCol = $col; + } + } + + $this->assertNotNull($hashCol); + $this->assertNotNull($dataCol); + $this->assertSame('binary', $hashCol->getType()); + $this->assertSame('binary', $dataCol->getType()); + $this->assertTrue($hashCol->getFixed()); + $this->assertNull($dataCol->getFixed()); + } } diff --git a/tests/TestCase/Db/Table/ColumnTest.php b/tests/TestCase/Db/Table/ColumnTest.php index 0652149f..2d5ccd1a 100644 --- a/tests/TestCase/Db/Table/ColumnTest.php +++ b/tests/TestCase/Db/Table/ColumnTest.php @@ -275,4 +275,56 @@ public function testUnsignedConfigurationDoesNotAffectNonIntegerTypes(): void $decimalColumn->setName('price')->setType('decimal'); $this->assertFalse($decimalColumn->isUnsigned()); } + + public function testFixedOptionDefaultsToNull(): void + { + $column = new Column(); + $column->setName('data')->setType('binary'); + + $this->assertNull($column->getFixed()); + } + + public function testSetFixedTrue(): void + { + $column = new Column(); + $column->setName('hash')->setType('binary')->setFixed(true); + + $this->assertTrue($column->getFixed()); + } + + public function testSetFixedFalse(): void + { + $column = new Column(); + $column->setName('data')->setType('binary')->setFixed(false); + + $this->assertFalse($column->getFixed()); + } + + public function testSetOptionsWithFixed(): void + { + $column = new Column(); + $column->setName('hash')->setType('binary'); + $column->setOptions(['fixed' => true, 'limit' => 20]); + + $this->assertTrue($column->getFixed()); + $this->assertSame(20, $column->getLimit()); + } + + public function testToArrayIncludesFixed(): void + { + $column = new Column(); + $column->setName('hash')->setType('binary')->setFixed(true)->setLimit(20); + + $result = $column->toArray(); + $this->assertTrue($result['fixed']); + } + + public function testToArrayFixedNullByDefault(): void + { + $column = new Column(); + $column->setName('data')->setType('binary')->setLimit(20); + + $result = $column->toArray(); + $this->assertNull($result['fixed']); + } } diff --git a/tests/TestCase/View/Helper/MigrationHelperTest.php b/tests/TestCase/View/Helper/MigrationHelperTest.php index c7800936..fd2dfe91 100644 --- a/tests/TestCase/View/Helper/MigrationHelperTest.php +++ b/tests/TestCase/View/Helper/MigrationHelperTest.php @@ -456,4 +456,38 @@ public function testGetColumnOptionConvertsCollateToCollation(): void $this->assertArrayHasKey('collation', $result, 'collation should be set from collate value'); $this->assertSame('en_US.UTF-8', $result['collation']); } + + /** + * Test that getColumnOption includes the fixed option for binary columns + */ + public function testGetColumnOptionIncludesFixed(): void + { + $options = [ + 'length' => 20, + 'null' => true, + 'default' => null, + 'fixed' => true, + ]; + + $result = $this->helper->getColumnOption($options); + + $this->assertArrayHasKey('fixed', $result); + $this->assertTrue($result['fixed']); + } + + /** + * Test that getColumnOption excludes fixed when not set + */ + public function testGetColumnOptionExcludesFixedWhenNotSet(): void + { + $options = [ + 'length' => 20, + 'null' => true, + 'default' => null, + ]; + + $result = $this->helper->getColumnOption($options); + + $this->assertArrayNotHasKey('fixed', $result); + } }