Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
25 changes: 25 additions & 0 deletions src/Builder/UuidValueBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Db\Sqlite\Builder;

use Yiisoft\Db\Constant\DataType;
use Yiisoft\Db\Expression\Value\Builder\UuidValueBuilder as BaseUuidValueBuilder;
use Yiisoft\Db\Expression\Value\Param;
use Yiisoft\Db\Expression\Value\UuidValue;
use Yiisoft\Db\Helper\DbUuidHelper;

/**
* Builds a {@see UuidValue} expression for SQLite.
*
* SQLite stores a UUID as 16 raw bytes in a `blob(16)` column, so the canonical string form is converted to bytes and
* bound as {@see DataType::LOB}.
*/
final class UuidValueBuilder extends BaseUuidValueBuilder
{
protected function prepareValue(UuidValue $expression): Param
{
return new Param(DbUuidHelper::uuidToBlob($expression->value), DataType::LOB);
Comment thread
Tigrov marked this conversation as resolved.
}
}
3 changes: 3 additions & 0 deletions src/DQLQueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -141,6 +143,7 @@ protected function defaultExpressionBuilders(): array
ArrayMerge::class => ArrayMergeBuilder::class,
Greatest::class => GreatestBuilder::class,
Least::class => LeastBuilder::class,
UuidValue::class => UuidValueBuilder::class,
];
}
}
79 changes: 79 additions & 0 deletions tests/Builder/UuidValueBuilderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Db\Sqlite\Tests\Builder;

use PHPUnit\Framework\Attributes\DataProvider;
use Yiisoft\Db\Constant\DataType;
use Yiisoft\Db\Expression\Value\Param;
use Yiisoft\Db\Expression\Value\UuidValue;
use Yiisoft\Db\Helper\DbUuidHelper;
use Yiisoft\Db\Sqlite\Builder\UuidValueBuilder;
use Yiisoft\Db\Sqlite\Tests\Support\IntegrationTestTrait;
use Yiisoft\Db\Tests\Support\IntegrationTestCase;

use function hex2bin;
use function strlen;

/**
* @group sqlite
*/
final class UuidValueBuilderTest extends IntegrationTestCase
{
use IntegrationTestTrait;

private const UUID = '738146be-87b1-49f2-9913-36142fb6fcbe';
private const HEX = '738146be87b149f2991336142fb6fcbe';

/**
* Every form {@see UuidValue} accepts, all denoting the same UUID.
*/
public static function values(): iterable
{
yield 'canonical' => [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');
}
}
Loading