From 64d2932e5d1a2002176c0f7e7bb8d38af3f5814b Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 6 Nov 2025 12:09:32 +0300 Subject: [PATCH 1/2] Fix typecasting bit columns' values with big size --- CHANGELOG.md | 1 + src/Column/BitColumn.php | 25 ++++++++++++++++++------- tests/ColumnTest.php | 14 ++++++++++++-- tests/Support/Fixture/pgsql.sql | 9 +++++++++ 4 files changed, 40 insertions(+), 9 deletions(-) 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..da78fb444 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..80d221693 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,14 @@ 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']); + } } diff --git a/tests/Support/Fixture/pgsql.sql b/tests/Support/Fixture/pgsql.sql index ebe84e1be..8eb11a69a 100644 --- a/tests/Support/Fixture/pgsql.sql +++ b/tests/Support/Fixture/pgsql.sql @@ -490,3 +490,12 @@ 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) +); +INSERT INTO "test_bit_column" ("bit_varying_64", "bit_varying_4") VALUES (B'1100000100011100100110001011000010100000001011001101111011100001', B'1001'); From d12122ccdff420b4157e9f018145b2413897da17 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 6 Nov 2025 12:14:38 +0300 Subject: [PATCH 2/2] fix --- src/Column/BitColumn.php | 4 ++-- tests/ColumnTest.php | 2 ++ tests/Support/Fixture/pgsql.sql | 6 ++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Column/BitColumn.php b/src/Column/BitColumn.php index da78fb444..bff532711 100644 --- a/src/Column/BitColumn.php +++ b/src/Column/BitColumn.php @@ -44,7 +44,7 @@ public function phpTypecast(mixed $value): int|string|null private function isBig(): bool { $size = $this->getSize(); - return $size !== null - && $size >= (PHP_INT_SIZE === 8 ? 64 : 32); + return $size === null + || $size >= (PHP_INT_SIZE === 8 ? 64 : 32); } } diff --git a/tests/ColumnTest.php b/tests/ColumnTest.php index 80d221693..6a942aaea 100644 --- a/tests/ColumnTest.php +++ b/tests/ColumnTest.php @@ -417,5 +417,7 @@ public function testBitColumnTypecasting(): void $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 8eb11a69a..32af9bb61 100644 --- a/tests/Support/Fixture/pgsql.sql +++ b/tests/Support/Fixture/pgsql.sql @@ -496,6 +496,8 @@ 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_4" BIT VARYING(4), + "bit_varying" BIT VARYING, + "bit" BIT ); -INSERT INTO "test_bit_column" ("bit_varying_64", "bit_varying_4") VALUES (B'1100000100011100100110001011000010100000001011001101111011100001', B'1001'); +INSERT INTO "test_bit_column" ("bit_varying_64", "bit_varying_4", "bit_varying", "bit") VALUES (B'1100000100011100100110001011000010100000001011001101111011100001', B'1001', B'101', B'1');