diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d6ee9ea..d78724c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Enh #414: Explicitly import classes and constants in "use" section (@mspirkov) - Enh #415: Remove unnecessary files from Composer package (@mspirkov) - Enh #416: Add `ext-pdo_sqlite` to `require` section of `composer.json` (@Tigrov) +- New #432: Add SQLite implementation of `UuidValue` expression (@KalimeroMK) ## 2.0.0 December 05, 2025 diff --git a/src/Builder/UuidValueBuilder.php b/src/Builder/UuidValueBuilder.php new file mode 100644 index 00000000..9878d2f4 --- /dev/null +++ b/src/Builder/UuidValueBuilder.php @@ -0,0 +1,25 @@ +value), DataType::LOB); + } +} diff --git a/src/DQLQueryBuilder.php b/src/DQLQueryBuilder.php index 808b3a9c..f68d0972 100644 --- a/src/DQLQueryBuilder.php +++ b/src/DQLQueryBuilder.php @@ -9,6 +9,7 @@ use Yiisoft\Db\Expression\Function\ArrayMerge; use Yiisoft\Db\Expression\Function\Greatest; use Yiisoft\Db\Expression\Function\Least; +use Yiisoft\Db\Expression\Value\UuidValue; use Yiisoft\Db\Query\Query; use Yiisoft\Db\Query\QueryInterface; use Yiisoft\Db\QueryBuilder\AbstractDQLQueryBuilder; @@ -23,6 +24,7 @@ use Yiisoft\Db\Sqlite\Builder\JsonOverlapsBuilder; use Yiisoft\Db\Sqlite\Builder\LeastBuilder; use Yiisoft\Db\Sqlite\Builder\LikeBuilder; +use Yiisoft\Db\Sqlite\Builder\UuidValueBuilder; use function array_filter; use function array_merge; @@ -141,6 +143,7 @@ protected function defaultExpressionBuilders(): array ArrayMerge::class => ArrayMergeBuilder::class, Greatest::class => GreatestBuilder::class, Least::class => LeastBuilder::class, + UuidValue::class => UuidValueBuilder::class, ]; } } diff --git a/tests/Builder/UuidValueBuilderTest.php b/tests/Builder/UuidValueBuilderTest.php new file mode 100644 index 00000000..d7d4db83 --- /dev/null +++ b/tests/Builder/UuidValueBuilderTest.php @@ -0,0 +1,79 @@ + [self::UUID]; + yield 'canonical in upper case' => ['738146BE-87B1-49F2-9913-36142FB6FCBE']; + yield 'hexadecimal' => [self::HEX]; + yield 'hexadecimal in upper case' => ['738146BE87B149F2991336142FB6FCBE']; + yield 'bytes' => [hex2bin(self::HEX)]; + } + + /** + * `UuidValue` normalizes the value to the canonical form on construction, so the builder binds the same 16 bytes + * whichever form it was created from. + */ + #[DataProvider('values')] + public function testBuildBindsRawBytesAsLob(string $value): void + { + $db = $this->getSharedConnection(); + $builder = new UuidValueBuilder($db->getQueryBuilder()); + + $params = []; + $result = $builder->build(new UuidValue($value), $params); + + $this->assertSame(':qp0', $result); + $this->assertEquals( + [':qp0' => new Param(DbUuidHelper::uuidToBlob(self::UUID), DataType::LOB)], + $params, + ); + } + + #[DataProvider('values')] + public function testInsertAndSelectUuid(string $value): void + { + $db = $this->getSharedConnection(); + + $this->dropTable('uuid_value'); + $this->executeStatements('CREATE TABLE [[uuid_value]] ([[id]] blob(16) NOT NULL)'); + + $db->createCommand()->insert('uuid_value', ['id' => new UuidValue($value)])->execute(); + + $bytes = $db->createCommand('SELECT [[id]] FROM [[uuid_value]]')->queryScalar(); + + $this->assertIsString($bytes); + $this->assertSame(16, strlen($bytes)); + $this->assertSame(self::UUID, DbUuidHelper::toUuid($bytes)); + + $this->dropTable('uuid_value'); + } +}