Skip to content

New #1152: Add UuidValue expression for DBMS-independent UUID values - #1199

Merged
Tigrov merged 5 commits into
yiisoft:masterfrom
KalimeroMK:add-1152-uuid-value
Sep 14, 2026
Merged

Tigrov merged 5 commits into
yiisoft:masterfrom
KalimeroMK:add-1152-uuid-value

Conversation

@KalimeroMK

@KalimeroMK KalimeroMK commented Sep 7, 2026

Copy link
Copy Markdown
Contributor
Q A
Is bugfix?
New feature? ✔️
Breaks BC?
Fixed issues #1152

Adds UuidValue, an expression that carries a UUID and is bound in the representation the current DBMS expects — raw bytes for MySQL, MariaDB, SQLite and Oracle, canonical string for PostgreSQL and MSSQL:

$db->createCommand()->insert('{{%page}}', ['id' => new UuidValue(Uuid::uuid7())])->execute();

It accepts the canonical form, 32 hexadecimal characters, 16 raw bytes or any Stringable. Passing raw bytes or a raw string directly keeps working as before.

…values

`ColumnBuilder::uuidPrimaryKey()` and `uuid()` already produce the right DDL
on every DBMS, but inserting a UUID required knowing which representation the
current connection expects: MySQL, MariaDB, SQLite and Oracle store a UUID as
16 raw bytes, while PostgreSQL and MSSQL expect the canonical string. The
official guide had to document the difference (yiisoft/docs#324) and
`CommonCommandTest::testUuid()` works around it with a per-driver `match`.

`UuidValue` carries the intent in the value instead of relying on the loaded
table schema. That matters because a UUID column reads back as `binary(16)`
on MySQL and `blob(16)` on SQLite, so a schema-driven typecast cannot tell it
apart from an ordinary binary column.

The value is normalized to the canonical lowercase form on construction, so
the canonical string, 32 hexadecimal characters, 16 raw bytes and any
`Stringable` returning one of those are all accepted — including
`Ramsey\Uuid\UuidInterface` itself.

`UuidValueBuilder` binds the canonical string, which is correct for
PostgreSQL and MSSQL. It is left non-final with a single `prepareValue()`
seam so drivers storing raw bytes override one method and reuse
`DbUuidHelper::uuidToBlob()`, which until now was unused by `src/`.

No behaviour changes for existing code: passing raw bytes or a raw string
keeps working exactly as before.
@codecov

codecov Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.62%. Comparing base (11032c2) to head (69c141d).

Additional details and impacted files
@@            Coverage Diff            @@
##             master    #1199   +/-   ##
=========================================
  Coverage     98.62%   98.62%           
- Complexity     1641     1644    +3     
=========================================
  Files           120      122    +2     
  Lines          4283     4291    +8     
=========================================
+ Hits           4224     4232    +8     
  Misses           59       59           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Tigrov Tigrov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a part of realization, with canonical string form. What about realizations for other concrete DBMS?

Comment thread src/Expression/Value/Builder/UuidValueBuilder.php Outdated
Comment thread src/Expression/Value/UuidValue.php Outdated
Return a `Param` from `UuidValueBuilder::prepareValue()` so DBMS that store
a UUID as raw bytes can bind it as `DataType::LOB` instead of letting
`buildValue()` infer `DataType::STRING` from the PHP type.

Reword the exception in `DbUuidHelper::toUuid()` and drop the extra
try/catch that wrapped it in `UuidValue`.
@KalimeroMK

KalimeroMK commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

@Tigrov correct, this is only the core half — the default builder covers PostgreSQL and MSSQL, while MySQL/MariaDB/SQLite/Oracle override prepareValue() to bind the raw bytes.

Correction to what I wrote earlier about CI: no tag is needed. install-packages resolves yiisoft/db from a same-named branch in my fork, so I can open the driver PRs from add-1152-uuid-value and they will be green against this branch right away. I will do that, so you can review the whole thing as one unit.

KalimeroMK pushed a commit to KalimeroMK/db-sqlite that referenced this pull request Sep 8, 2026
SQLite stores a UUID as 16 raw bytes in a `blob(16)` column, so
`prepareValue()` converts the canonical form with
`DbUuidHelper::uuidToBlob()` and binds it as `DataType::LOB`.

Requires yiisoft/db#1199.
KalimeroMK pushed a commit to KalimeroMK/db-mysql that referenced this pull request Sep 8, 2026
MySQL and MariaDB store a UUID as 16 raw bytes in a `binary(16)` column,
so `prepareValue()` converts the canonical form with
`DbUuidHelper::uuidToBlob()` and binds it as `DataType::LOB`.

Requires yiisoft/db#1199.
KalimeroMK pushed a commit to KalimeroMK/db-oracle that referenced this pull request Sep 8, 2026
Oracle stores a UUID as 16 raw bytes in a `raw(16)` column, so
`prepareValue()` converts the canonical form with
`DbUuidHelper::uuidToBlob()` and binds it as `DataType::LOB`.

Requires yiisoft/db#1199.
@KalimeroMK

KalimeroMK commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

@Tigrov driver PRs are up, green against this branch on real databases:

db-pgsql and db-mssql need nothing.

Oracle could not use the prepareValue() seam: PDO_OCI inserts NULL for a raw(16) column bound as DataType::LOB, so it emits a HEXTORAW() literal instead, as prepareBinary() already does there.

Correction to my earlier note about CI: composer-dependency-analyser does a plain composer install, so it stays red in all three until 2.0.2 is tagged and the drivers bump yiisoft/db to ^2.0.2. I will push that bump when you tag.

@KalimeroMK

Copy link
Copy Markdown
Contributor Author

@Tigrov both review comments are addressed in 6795ad2 and CI is green (82 checks, mergeable).

Could you merge this and tag 2.0.2? The three driver PRs — yiisoft/db-sqlite#432, yiisoft/db-mysql#482, yiisoft/db-oracle#411 — are ready and only waiting on that tag: their composer-dependency-analyser failures clear as soon as UuidValue exists in a released version.

Comment thread tests/Db/Expression/Value/Builder/UuidValueBuilderTest.php Outdated
@Tigrov
Tigrov requested a review from a team September 13, 2026 02:03
Comment thread src/Expression/Value/Builder/UuidValueBuilder.php Outdated
DBMS-specific packages now implement ExpressionBuilderInterface directly
instead of extending this builder, which keeps the coupling between the
core and the driver packages at the interface.
@vjik
vjik requested a review from Tigrov September 13, 2026 07:58
@vjik vjik added the status:code review The pull request needs review. label Sep 13, 2026
Comment thread src/Expression/Value/Builder/UuidValueBuilder.php Outdated
@Tigrov
Tigrov merged commit ccc9ed1 into yiisoft:master Sep 14, 2026
82 checks passed
Tigrov pushed a commit to yiisoft/db-sqlite that referenced this pull request Sep 14, 2026
* New: Add SQLite implementation of `UuidValue` expression

SQLite stores a UUID as 16 raw bytes in a `blob(16)` column, so
`prepareValue()` converts the canonical form with
`DbUuidHelper::uuidToBlob()` and binds it as `DataType::LOB`.

Requires yiisoft/db#1199.

* Rename the base-class alias and guard the read-back type in the test

* Cover every accepted UUID input form in the builder test

* Implement ExpressionBuilderInterface directly in UuidValueBuilder

The core UuidValueBuilder is final now, so this builder no longer extends
it. Dropping the inheritance keeps the coupling with the core package at
the interface.

* Apply Rector: use null coalescing assignment

* Use bindParam() instead of buildValue() in UuidValueBuilder

---------

Co-authored-by: zoran <zoran.bogoevski@zitcha.com>
Co-authored-by: Sergei Predvoditelev <sergei@predvoditelev.ru>
Tigrov pushed a commit to yiisoft/db-mysql that referenced this pull request Sep 14, 2026
* New: Add MySQL implementation of `UuidValue` expression

MySQL and MariaDB store a UUID as 16 raw bytes in a `binary(16)` column,
so `prepareValue()` converts the canonical form with
`DbUuidHelper::uuidToBlob()` and binds it as `DataType::LOB`.

Requires yiisoft/db#1199.

* Rename the base-class alias and guard the read-back type in the test

* Cover every accepted UUID input form in the builder test

* Implement ExpressionBuilderInterface directly in UuidValueBuilder

The core UuidValueBuilder is final now, so this builder no longer extends
it. Dropping the inheritance keeps the coupling with the core package at
the interface.

* Use bindParam() instead of buildValue() in UuidValueBuilder

---------

Co-authored-by: zoran <zoran.bogoevski@zitcha.com>
Co-authored-by: Sergei Predvoditelev <sergei@predvoditelev.ru>
Tigrov pushed a commit to yiisoft/db-oracle that referenced this pull request Sep 14, 2026
* New: Add Oracle implementation of `UuidValue` expression

Oracle stores a UUID as 16 raw bytes in a `raw(16)` column, so
`prepareValue()` converts the canonical form with
`DbUuidHelper::uuidToBlob()` and binds it as `DataType::LOB`.

Requires yiisoft/db#1199.

* Fix: emit `HEXTORAW()` literal instead of binding a LOB parameter

CI showed PDO_OCI inserts NULL when the raw bytes are bound as a large
object, so build the `HEXTORAW()` literal directly — the representation
this driver already uses for binary values.

* Fix: accept both `raw` read formats in the UUID round-trip test

* Fix: compare the read-back UUID case-insensitively

* Rename the base-class alias and guard the read-back type in the test

* Cover every accepted UUID input form in the builder test

* Implement ExpressionBuilderInterface directly in UuidValueBuilder

The core UuidValueBuilder is final now, so this builder no longer extends
it. Dropping the inheritance keeps the coupling with the core package at
the interface.

---------

Co-authored-by: zoran <zoran.bogoevski@zitcha.com>
Co-authored-by: Sergei Predvoditelev <sergei@predvoditelev.ru>
@Tigrov

Tigrov commented Sep 14, 2026

Copy link
Copy Markdown
Member

@KalimeroMK Thanks 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status:code review The pull request needs review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants