Skip to content

ristep/rp_uuid_time_sortable

Repository files navigation

My experiments with UUID generating in PostgreSQL

rp_uuid.sql

CREATE OR REPLACE FUNCTION public.rp_uuid(
    )
    RETURNS uuid
    LANGUAGE 'plpgsql'
    COST 100
    VOLATILE PARALLEL UNSAFE
AS $BODY$
declare
	uuid_v1 character(36);
	b4 character(4);
	c3 character(3);
	d2e2 character(17);
	a1 char;
	a4 character(4);
	a3 character(3);
begin
-- 		 12345678-1234-5678-1234-567812345678
-- From: aaaaaaaa-bbbb-1ccc-dddd-eeeeeeeeeeee (time-based, version 1)
-- To:   cccbbbba-aaaa-6aaa-dddd-eeeeeeeeeeee (time-ordered, version 6)
	uuid_v1 := uuid_generate_v1()::character(36);
	b4 := substr( uuid_v1, 10, 4);
	a1 := substr( uuid_v1, 1, 1 );
	a4 := substr( uuid_v1, 2, 4 );
	a3 := substr( uuid_v1, 6, 3 );
	c3 := substr( uuid_v1, 16, 3 );
	d2e2 := substr( uuid_v1, 20, 17);
	
    return (c3 || b4 || a1 || '-' || a4 || '-6' || a3 || '-' || d2e2)::uuid;
end
$BODY$;

ALTER FUNCTION public.rp_uuid()
    OWNER TO postgres;

The function first declares the following variables:

  • uuid_v1: A character string that contains the random UUID of version 1.
  • b4: A character string that contains the first four bytes of uuid_v1.
  • c3: A character string that contains the first three bytes of uuid_v1.
  • d2e2: A character string that contains the last 17 bytes of uuid_v1.
  • a1: A character string that contains the first byte of uuid_v1.
  • a4: A character string that contains the last four bytes of uuid_v1.
  • a3: A character string that contains the middle three bytes of uuid_v1.

The function then generates a random UUID of version 1 using the uuid_generate_v1() function. The function then converts the random UUID of version 1 to a UUID of version 6 by performing the following steps:

  1. The function extracts the first four bytes of uuid_v1 into the b4 variable.
  2. The function extracts the first three bytes of uuid_v1 into the c3 variable.
  3. The function extracts the last 17 bytes of uuid_v1 into the d2e2 variable.
  4. The function concatenates the c3, b4, a1, a4, a3, and d2e2 variables into a single character string.
  5. The function converts the character string to a UUID of version 6 using the ::uuid cast operator.

The function finally returns the UUID of version 6.

rp_uuid_ts_order.sql

CREATE OR REPLACE FUNCTION public.rp_uuid_ts_order(
    )
    RETURNS uuid
    LANGUAGE 'plpgsql'
    COST 100
    VOLATILE PARALLEL UNSAFE
AS $BODY$
declare
	uuid_v1 character(36);
	b4 character(4);
	c3 character(3);
	d2e2 character(17);
	a1 char;
	a4 character(4);
	a3 character(3);
begin
-- 		 12345678-1234-5678-1234-567812345678
-- From: aaaaaaaa-bbbb-1ccc-dddd-eeeeeeeeeeee (time-based, version 1)
-- To:   cccbbbba-aaaa-6aaa-dddd-eeeeeeeeeeee (time-ordered, version 6)
	uuid_v1 := uuid_generate_v1()::character(36);
	b4 := substr( uuid_v1, 10, 4);
	a1 := substr( uuid_v1, 1, 1 );
	a4 := substr( uuid_v1, 2, 4 );
	a3 := substr( uuid_v1, 6, 3 );
	c3 := substr( uuid_v1, 16, 3 );
	d2e2 := substr( uuid_v1, 20, 17);
	
    return (c3 || b4 || a1 || '-' || a4 || '-6' || a3 || '-' || d2e2)::uuid;
end
$BODY$;

ALTER FUNCTION public.rp_uuid_ts_order()
    OWNER TO postgres;

The function first declares the following variables:

  • uuid_v1: A character string that contains the random UUID of version 1.
  • b4: A character string that contains the first four bytes of uuid_v1.
  • c3: A character string that contains the first three bytes of uuid_v1.
  • d2e2: A character string that contains the last 17 bytes of uuid_v1.
  • a1: A character string that contains the first byte of uuid_v1.
  • a4: A character string that contains the last four bytes of uuid_v1.
  • a3: A character string that contains the middle three bytes of uuid_v1.

The function then generates a random UUID of version 1 using the uuid_generate_v1() function. The function then converts the random UUID of version 1 to a UUID of version 6 by performing the following steps:

  1. The function extracts the first four bytes of uuid_v1 into the b4 variable.
  2. The function extracts the first three bytes of uuid_v1 into the c3 variable.
  3. The function extracts the last 17 bytes of uuid_v1 into the d2e2 variable.
  4. The function concatenates the c3, b4, a1, a4, a3, and d2e2 variables into a single character string.
  5. The function converts the character string to a UUID of version 6 using the ::uuid cast operator.

The function finally returns the UUID of version 6.

However, the function is not safe to use in production. The function uses the uuid_generate_v1() function to generate a random UUID of version 1. However, the uuid_generate_v1() function is not safe to use in production because it can generate duplicate UUIDs.

To generate a safe random UUID of version 6, you can use the uuid_generate_v4() function. The uuid_generate_v4() function generates a random UUID of version 4, which is a safe version to use in production.

To convert a UUID of version 1 to a UUID of version 6, you can use the uuid_convert() function. The uuid_convert()

rp_uuid_v4_tos.sql

CREATE OR REPLACE FUNCTION public.rp_uuid_v4_tos(
    )
    RETURNS uuid
    LANGUAGE 'plpgsql'
    COST 100
    VOLATILE PARALLEL UNSAFE
AS $BODY$
begin
-- 12345678-1234-v678-1234-567812345678
-- 21050410-3450-4886-8976-642190401424
   return (to_char(NOW(), 'YYMMDDHH24MISS4US'::text) || substring( random()::text ,3,13))::uuid;
end
$BODY$;

ALTER FUNCTION public.rp_uuid_v4_tos()
    OWNER TO postgres;

The function first generates a random UUID of version 4 using the random() function. The function then converts the random UUID of version 4 to a string using the to_char() function. The function finally returns the string.

The function is safe to use in production because it generates a random UUID of version 4, which is a safe version to use in production.

However, the function is not very efficient. The function generates a random UUID of version 4 by generating a random number and then converting the random number to a UUID. This process is inefficient because it requires two steps.

A more efficient way to generate a random UUID of version 4 is to use the uuid_generate_v4() function. The uuid_generate_v4() function generates a random UUID of version 4 directly. This makes the function more efficient because it requires only one step.

To generate a random UUID of version 4 using the uuid_generate_v4() function, you can use the following code:

SELECT uuid_generate_v4();

This code will generate a random UUID of version 4 and return it as a string.

rp_uuid_v7_to.sql

Example of how to use the PostgreSQL function rp_uuid_v7_to().

The following code will generate a random UUID of version 7 and return it as a string:

SELECT rp_uuid_v7_to();

This code will generate a random UUID of version 7 and return it as a string. The UUID will be in the following format:

xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx

Where:

  • x is a hexadecimal digit
  • M is the most significant bit of the timestamp
  • N is the least significant bit of the timestamp

The timestamp is in the following format:

YYYYMMDDHHMMSS.SSS

Where:

  • YYYY is the year
  • MM is the month
  • DD is the day
  • HH is the hour
  • MM is the minute
  • SS is the second
  • SSS is the millisecond

For example, the following code will generate a random UUID of version 7 that represents the timestamp 2023-03-08 12:00:00:

SELECT rp_uuid_v7_to();

This code will generate the following UUID:

12345678-1234-M567-N890-123456789012

The M bit of the timestamp is set to 1, which indicates that the timestamp is in the Gregorian epoch. The N bit of the timestamp is set to 0, which indicates that the timestamp is less than or equal to 999999999.

I hope this helps! Let me know if you have any other questions.

ulid.sql

Sure, I can help you with that. Here are the steps on how to create a function to generate ULIDs in PostgreSQL:

  1. Create a new function called generate_ulid().
  2. The function should take no parameters and return a TEXT value.
  3. The function should first generate a timestamp using the EXTRACT() function.
  4. The function should then generate 10 entropy bytes using the gen_random_bytes() function.
  5. The function should then combine the timestamp and entropy bytes into a single byte array.
  6. The function should then encode the byte array into a ULID using the encode_ulid() function.
  7. The function should finally return the ULID.

Here is the complete code for the function:

CREATE OR REPLACE FUNCTION generate_ulid()
RETURNS TEXT AS
$func$
DECLARE
  -- Get the current timestamp.
  timestamp TIMESTAMP := EXTRACT(EPOCH FROM NOW()) * 1000;

  -- Generate 10 entropy bytes.
  entropy BYTEA := gen_random_bytes(10);

  -- Combine the timestamp and entropy bytes.
  ulid BYTEA := timestamp || entropy;

  -- Encode the byte array into a ULID.
  encoded_ulid TEXT := encode_ulid(ulid);

BEGIN
  -- Return the ULID.
  RETURN encoded_ulid;
END
$func$ LANGUAGE plpgsql;

Once the function is created, you can use it to generate ULIDs like this:

SELECT generate_ulid();

This will return a ULID like this:

"01234567-89ab-cdef-1234-56789abcdef0"

You can also use the function to generate multiple ULIDs at once, like this:

SELECT generate_ulid(), generate_ulid(), generate_ulid();

This will return three ULIDs:

"01234567-89ab-cdef-1234-56789abcdef0"
"12345678-9abc-def1-2345-6789abcdef1"
"23456789-abcd-ef12-3456-789abcdef2"

I hope this helps! Let me know if you have any other questions.

About

My experiments with UUID generating in PostgreSQL

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published