propelorm / Propel2

Propel2 is an open-source high-performance Object-Relational Mapping (ORM) for modern PHP
http://propelorm.org/
MIT License
1.26k stars 393 forks source link

Add type to store UUIDs as binary (UUID_BINARY) #1917

Closed mringler closed 1 year ago

mringler commented 1 year ago

This allows Propel to work with UUIDs stored in a binary data column like BINARY(16) in MySQL.

To use it, the column type in schema.xml has to be set to UUID_BINARY:

    <table name="my_table">
        <column name="uuid" required="false" type="UUID_BINARY"/>
    </table>

In models, fields of that type will always contain the UUID as string, conversion between binary value and string is done during loading (in hydrate()) or saving (in doInsert() or buildCriteria() from doUpdate()). Similar conversion happens in the findBy methods in the query class. This is a different approach than planned in #1914, but I found it integrates better into Propel.

Per default, UUID conversion will swap some bytes around, in accordance with MySQL's uuid_to_bin() function. This allows better indexing for version-1 UUIDs. The default behavior can be changed in schema.xml by setting the value of UuidSwapFlag in vendor information:

<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<database name="bookstore" defaultIdMethod="native"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    namespace="Propel\Tests\Bookstore"
>
    <vendor type="mysql">
          <parameter name="UuidSwapFlag" value="false"/>
    </vendor>

Caveats:

Realistically, this will take a brave user to test and give feedback.

dereuromark commented 1 year ago

Great work!

type="UUID_BINARY"

vs https://github.com/propelorm/Propel2/pull/1915 and type="UUID"

My main question would be: Do we need to have different type names per DB? From a user perspective the internals (and custom decisions on the specific sub type) are not so interesting usually, so lets say you want to use UUID from a framework perspective and support 3+ DB types The ORM ideally abstracts the internals away, that you can specify UUID across your project definitions then and they dont have to be different per DB type.

In other words: Ideally we define

type="UUID"

and by default picks the best internal strategy by default for each ORM DB type supported.

Is that something we can achieve?

codecov-commenter commented 1 year ago

Codecov Report

Base: 88.44% // Head: 73.25% // Decreases project coverage by -15.19% :warning:

Coverage data is based on head (ce0a677) compared to base (b35fb89). Patch coverage: 65.73% of modified lines in pull request are covered.

Additional details and impacted files ```diff @@ Coverage Diff @@ ## master #1917 +/- ## ============================================= - Coverage 88.44% 73.25% -15.20% - Complexity 7854 7917 +63 ============================================= Files 224 227 +3 Lines 20988 21133 +145 ============================================= - Hits 18562 15480 -3082 - Misses 2426 5653 +3227 ``` | Flag | Coverage Δ | | |---|---|---| | 5-max | `73.25% <65.73%> (-15.20%)` | :arrow_down: | | 7.4 | `73.25% <65.73%> (-15.20%)` | :arrow_down: | | agnostic | `?` | | | mysql | `68.95% <62.94%> (-0.14%)` | :arrow_down: | | pgsql | `69.05% <56.57%> (-0.01%)` | :arrow_down: | | sqlite | `66.96% <51.39%> (+0.03%)` | :arrow_up: | Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=propelorm#carryforward-flags-in-the-pull-request-comment) to find out more. | [Impacted Files](https://codecov.io/gh/propelorm/Propel2/pull/1917?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=propelorm) | Coverage Δ | | |---|---|---| | [src/Propel/Generator/Model/ForeignKey.php](https://codecov.io/gh/propelorm/Propel2/pull/1917/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=propelorm#diff-c3JjL1Byb3BlbC9HZW5lcmF0b3IvTW9kZWwvRm9yZWlnbktleS5waHA=) | `89.73% <ø> (-4.97%)` | :arrow_down: | | [src/Propel/Generator/Model/PropelTypes.php](https://codecov.io/gh/propelorm/Propel2/pull/1917/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=propelorm#diff-c3JjL1Byb3BlbC9HZW5lcmF0b3IvTW9kZWwvUHJvcGVsVHlwZXMucGhw) | `85.71% <ø> (-14.29%)` | :arrow_down: | | [src/Propel/Generator/Model/Schema.php](https://codecov.io/gh/propelorm/Propel2/pull/1917/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=propelorm#diff-c3JjL1Byb3BlbC9HZW5lcmF0b3IvTW9kZWwvU2NoZW1hLnBocA==) | `50.00% <0.00%> (-47.71%)` | :arrow_down: | | [...erator/Platform/Util/MysqlUuidMigrationBuilder.php](https://codecov.io/gh/propelorm/Propel2/pull/1917/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=propelorm#diff-c3JjL1Byb3BlbC9HZW5lcmF0b3IvUGxhdGZvcm0vVXRpbC9NeXNxbFV1aWRNaWdyYXRpb25CdWlsZGVyLnBocA==) | `0.00% <0.00%> (ø)` | | | [src/Propel/Generator/Reverse/MysqlSchemaParser.php](https://codecov.io/gh/propelorm/Propel2/pull/1917/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=propelorm#diff-c3JjL1Byb3BlbC9HZW5lcmF0b3IvUmV2ZXJzZS9NeXNxbFNjaGVtYVBhcnNlci5waHA=) | `88.26% <ø> (ø)` | | | [src/Propel/Runtime/Map/ColumnMap.php](https://codecov.io/gh/propelorm/Propel2/pull/1917/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=propelorm#diff-c3JjL1Byb3BlbC9SdW50aW1lL01hcC9Db2x1bW5NYXAucGhw) | `55.44% <0.00%> (-25.75%)` | :arrow_down: | | [src/Propel/Generator/Model/Column.php](https://codecov.io/gh/propelorm/Propel2/pull/1917/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=propelorm#diff-c3JjL1Byb3BlbC9HZW5lcmF0b3IvTW9kZWwvQ29sdW1uLnBocA==) | `80.65% <57.14%> (-16.85%)` | :arrow_down: | | [src/Propel/Runtime/Util/UuidConverter.php](https://codecov.io/gh/propelorm/Propel2/pull/1917/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=propelorm#diff-c3JjL1Byb3BlbC9SdW50aW1lL1V0aWwvVXVpZENvbnZlcnRlci5waHA=) | `66.66% <66.66%> (ø)` | | | [...rc/Propel/Generator/Model/Diff/TableComparator.php](https://codecov.io/gh/propelorm/Propel2/pull/1917/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=propelorm#diff-c3JjL1Byb3BlbC9HZW5lcmF0b3IvTW9kZWwvRGlmZi9UYWJsZUNvbXBhcmF0b3IucGhw) | `96.74% <75.00%> (-3.26%)` | :arrow_down: | | [src/Propel/Generator/Model/Table.php](https://codecov.io/gh/propelorm/Propel2/pull/1917/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=propelorm#diff-c3JjL1Byb3BlbC9HZW5lcmF0b3IvTW9kZWwvVGFibGUucGhw) | `73.47% <79.16%> (-17.94%)` | :arrow_down: | | ... and [135 more](https://codecov.io/gh/propelorm/Propel2/pull/1917/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=propelorm) | | Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=propelorm). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=propelorm)

:umbrella: View full report at Codecov.
:loudspeaker: Do you have feedback about the report comment? Let us know in this issue.

mringler commented 1 year ago

Ok, now UUID_BINARY is used as fallback type for UUID in MySQL and SQLite (all other vendors have a native UUID type). So now the UUID type can always be used (even though the result might be surprising).

I think we should keep both types, as it allows to still use UUID_BINARY on systems where UUID maps to the native type. This might be useful if an old version of the dbms is used or if the db already uses binary columns and cannot be changed. Also, if you know your database will use UUID_BINARY anyway, you might find it cleaner to just specify that. And should MySQL ever support a native UUID type and we change the behavior of UUID, users can switch to UUID_BINARY if they want to avoid migrating their db.

@dereuromark what do you think does that make sense?

dereuromark commented 1 year ago

Unsafe usage of new static().

These should be in phpstan ignoreErrors list.

mringler commented 1 year ago

I have added code for automatic migration between CHAR/VARCHAR and BINARY uuid columns on MySQL. If column type is changed in schema.xml, Propel will add code to the migration file, which creates a new column for the converted values and then replaces the old column with that new one.

When changing from a binary column to a CHAR type, propel does not know that the column contains uuids, it just sees binary to char. To let Propel know that it is a conversion of uuids, the column has to be marked as such by adding the content="UUID" to the declaration in schema.xml (we can use something else if that is better):

<column name="id" type="varchar" size="36" content="UUID" />

I had to change some overall behavior to get this working, my assumption is that these changes are useful:

With the test, I am becoming more confident that this might actually work. Let me know what you think!