Skip to content

1280 UUID v7 support#1284

Draft
dantownsend wants to merge 18 commits intomasterfrom
1280-uuidv7
Draft

1280 UUID v7 support#1284
dantownsend wants to merge 18 commits intomasterfrom
1280-uuidv7

Conversation

@dantownsend
Copy link
Member

Resolves #1280

Comment on lines 50 to 51
Copy link
Member Author

Choose a reason for hiding this comment

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

Cockroach doesn't have UUID 7 support yet (as far as I can see).

I guess we can just raise NotImplemented for now.

Comment on lines 11 to 26
def postgres(self):
return "uuid_generate_v4()"
"""
Historically we had to use `uuid_generate_v4()` from the `uuid-ossp`
extension.

Since Postgres 13 there is a built-in `gen_random_uuid` function which
generates UUID v4 values.

In Postgres 18, `uuidv4` was added, which is the same as
`gen_random_uuid`, but more precisely named. We will move to this at
some point in the future.

"""
return "gen_random_uuid()"
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm going to split these changes out into a separate PR.

Copy link
Member

Choose a reason for hiding this comment

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

This is done with #1323

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks

@sinisaos
Copy link
Member

@dantownsend Just one question. How would you solve this Postgres error without having to install some uuidv7 extension. Do you have another plan?

@dantownsend
Copy link
Member Author

@dantownsend Just one question. How would you solve this Postgres error without having to install some uuidv7 extension. Do you have another plan?

I was going to just put in the docs that it's Postgres 18 only for now.

I think there are some scripts out there somewhere which would allow us to use uuid 7 on older versions. If you can find one I'm not averse to using it (it might also solve the problem for CockroachDB too).

@sinisaos
Copy link
Member

I tried this custom function and by adding this function I was able to pass the tests. I don't know if this is the correct way to use it, but I added that custom function to postgres.py like this

if current_transaction:
    await current_transaction.connection.execute(
        """
        -- Source: https://gist.github.com/kjmph/5bd772b2c2df145aa645b837da7eca74
        create or replace function uuidv7()
        returns uuid
        as $$
        begin
        -- use random v4 uuid as starting point (which has the same variant we need)
        -- then overlay timestamp
        -- then set version 7 by flipping the 2 and 1 bit in the version 4 string
        return encode(
            set_bit(
            set_bit(
                overlay(uuid_send(gen_random_uuid())
                        placing substring(int8send(floor(extract(epoch from clock_timestamp()) * 1000)::bigint) from 3)
                        from 1 for 6
                ),
                52, 1
            ),
            53, 1
            ),
            'hex')::uuid;
        end
        $$
        language plpgsql
        volatile;
        """
    )
    response = await current_transaction.connection.fetch(ddl)

I also tried those changes in the app using id = UUID(primary_key=True, default=UUID7()) and it worked. UUID7 is generated correctly.

@dantownsend
Copy link
Member Author

I tried this custom function and by adding this function I was able to pass the tests. I don't know if this is the correct way to use it, but I added that custom function to postgres.py like this

if current_transaction:
    await current_transaction.connection.execute(
        """
        -- Source: https://gist.github.com/kjmph/5bd772b2c2df145aa645b837da7eca74
        create or replace function uuidv7()
        returns uuid
        as $$
        begin
        -- use random v4 uuid as starting point (which has the same variant we need)
        -- then overlay timestamp
        -- then set version 7 by flipping the 2 and 1 bit in the version 4 string
        return encode(
            set_bit(
            set_bit(
                overlay(uuid_send(gen_random_uuid())
                        placing substring(int8send(floor(extract(epoch from clock_timestamp()) * 1000)::bigint) from 3)
                        from 1 for 6
                ),
                52, 1
            ),
            53, 1
            ),
            'hex')::uuid;
        end
        $$
        language plpgsql
        volatile;
        """
    )
    response = await current_transaction.connection.fetch(ddl)

I also tried those changes in the app using id = UUID(primary_key=True, default=UUID7()) and it worked. UUID7 is generated correctly.

Thanks for this. I'm still a bit undecided how deep to go on this. Maybe we add this to the docs if people want to use UUID7 on older versions of Postgres.

@sinisaos
Copy link
Member

Here is another example of a custom Postgres uuid v7 function (from Piccolo issues). What I like about uuid v7 is they are sequential (similar to Serial column ). Cockroach does not work with uuid v7 in my case.

log_queries: bool = False,
log_responses: bool = False,
extra_nodes: Optional[Mapping[str, PostgresEngine]] = None,
polyfills: list[Literal["uuidv7"]] = [],
Copy link
Member

Choose a reason for hiding this comment

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

This is great, really clever. Can you add a description of polyfills to the PostgresEngine docstring?

Comment on lines +60 to +65
@property
def cockroach(self):
"""
Unfortunately CockroachDB doesn't current support this.
"""
raise NotImplementedError()
Copy link
Member

Choose a reason for hiding this comment

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

I don't know if it's a good idea, but since Cockroach doesn't natively support uuidv7, we can generate default values ​​from Python. Something like this

@property
def cockroach(self):
    return QueryString(f"'{uuid7()}'")

I tried it locally with Cockroach and it worked. A valid uuidv7 is generated and saved in the database. Piccolo unittests also pass.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ideally I'd love to find a script which provides UUID7 for Cockroach. I can't get the Postgres one to work with Cockroach:

ERROR: encode(): set_bit(): set_bit(): unknown signature: overlay(bytes, bytes, int, int)
SQLSTATE: 42883
HINT: No function matches the given name and argument types. You might need to add explicit type casts.

Even though this works:

@property
def cockroach(self):
    return QueryString(f"'{uuid7()}'")

We need a function which generates the UUID within the database itself. For now I think I'll just leave out Cockroach support until they natively support it.

Copy link
Member

Choose a reason for hiding this comment

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

Never mind, it was just an idea. I haven't found any examples of custom functions that will work in Cockroach, which means it's not that common in Cockroach. You are right that it should be left as it is and wait for the native implementation.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, it's hard with Cockroach because it's mostly Postgres compatible, but when it comes to functions etc it's missing a few.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm tempted to not even support older versions of Postgres and Python with uuid7, just because it relies on other people's code (the uuid7 Python backport, and the Postgres function), which are under different licenses. It becomes a bit of a mess.

So if people want to use it, they just have to use Python 3.14 and Postgres 18.

We'll just make the tests only run for that version of Python and Postgres.

Copy link
Member

Choose a reason for hiding this comment

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

It's actually a good idea and the code will be much simpler. With this we only rely on the Python 3.14 standard library and the native Postgres 18 implementation. That should be emphasized in the documentation.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

UUID v7 support

2 participants

Comments