diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f84beb42..437119216 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ - Enh #442: Refactor `DMLQueryBuilder::upsert()` method (@Tigrov) - Enh #444: Improve `ArrayExpressionBuilder` and `JsonExpressionBuilder` classes (@Tigrov) - Chg #447: Update expression namespaces according to changes in `yiisoft/db` package (@Tigrov) +- Bug #455: Fix typecasting bit columns' values with big size (@vjik) ## 1.3.0 March 21, 2024 diff --git a/src/Column/BitColumn.php b/src/Column/BitColumn.php index f2cc4b34b..bff532711 100644 --- a/src/Column/BitColumn.php +++ b/src/Column/BitColumn.php @@ -4,16 +4,20 @@ namespace Yiisoft\Db\Pgsql\Column; +use Yiisoft\Db\Constant\ColumnType; use Yiisoft\Db\Expression\ExpressionInterface; -use Yiisoft\Db\Schema\Column\BitColumn as BaseBitColumn; +use Yiisoft\Db\Schema\Column\AbstractColumn; use function bindec; use function decbin; use function gettype; +use function is_string; use function str_pad; -final class BitColumn extends BaseBitColumn +final class BitColumn extends AbstractColumn { + protected const DEFAULT_TYPE = ColumnType::BIT; + /** @psalm-suppress RedundantCast */ public function dbTypecast(mixed $value): string|ExpressionInterface|null { @@ -26,14 +30,21 @@ public function dbTypecast(mixed $value): string|ExpressionInterface|null }; } - public function phpTypecast(mixed $value): int|null + public function phpTypecast(mixed $value): int|string|null { /** @var int|string|null $value */ - if (is_string($value)) { - /** @var int */ - return bindec($value); + if ($this->isBig() || !is_string($value)) { + return $value; } - return $value; + /** @var int */ + return bindec($value); + } + + private function isBig(): bool + { + $size = $this->getSize(); + return $size === null + || $size >= (PHP_INT_SIZE === 8 ? 64 : 32); } } diff --git a/tests/ColumnTest.php b/tests/ColumnTest.php index fff266486..6a942aaea 100644 --- a/tests/ColumnTest.php +++ b/tests/ColumnTest.php @@ -383,7 +383,7 @@ public function testPhpTypecastArrayColumn(ColumnInterface $column, array $value } } - public function testIntegerColumn() + public function testIntegerColumn(): void { $intCol = new IntegerColumn(); @@ -396,7 +396,7 @@ public function testIntegerColumn() $this->assertNull($intCol->getSequenceName()); } - public function testBigIntColumn() + public function testBigIntColumn(): void { $bigintCol = new BigIntColumn(); @@ -408,4 +408,16 @@ public function testBigIntColumn() $this->assertNull($bigintCol->getSequenceName()); } + + public function testBitColumnTypecasting(): void + { + $db = $this->getConnection(true); + + $row = $db->select()->from('test_bit_column')->where(['id' => 1])->withTypecasting()->one(); + + $this->assertSame('1100000100011100100110001011000010100000001011001101111011100001', $row['bit_varying_64']); + $this->assertSame(9, $row['bit_varying_4']); + $this->assertSame('101', $row['bit_varying']); + $this->assertSame(1, $row['bit']); + } } diff --git a/tests/Support/Fixture/pgsql.sql b/tests/Support/Fixture/pgsql.sql index ebe84e1be..32af9bb61 100644 --- a/tests/Support/Fixture/pgsql.sql +++ b/tests/Support/Fixture/pgsql.sql @@ -490,3 +490,14 @@ CREATE TABLE "test_structured_type" "price_array2" "currency_money_structured"[][], "range_price_col" "range_price_structured" DEFAULT '("(0,USD)","(100,USD)")' ); + +DROP TABLE IF EXISTS "test_bit_column"; +CREATE TABLE "test_bit_column" +( + "id" SERIAL NOT NULL PRIMARY KEY, + "bit_varying_64" BIT VARYING(64), + "bit_varying_4" BIT VARYING(4), + "bit_varying" BIT VARYING, + "bit" BIT +); +INSERT INTO "test_bit_column" ("bit_varying_64", "bit_varying_4", "bit_varying", "bit") VALUES (B'1100000100011100100110001011000010100000001011001101111011100001', B'1001', B'101', B'1');