Uuid_v7_primary_key :id throws: (undefined_function) function uuid_generate_v7() while migrating

I just attempted to change all the to uuid_primary_key to uuid_v7_primary_key and now it is throwing the following error. I followed the documentation and ensured to have "uuid-ossp" in my Repo’s installed_extensions/0.

What could I be missing?

22:31:50.622 [info] create table users
** (Postgrex.Error) ERROR 42883 (undefined_function) function uuid_generate_v7() does not exist

    hint: No function matches the given name and argument types. You might need to add explicit type casts.
    (ecto_sql 3.12.1) lib/ecto/adapters/sql.ex:1096: Ecto.Adapters.SQL.raise_sql_call_error/1

If I would guess, I think you are missing "ash-functions" in the installed_extensions/0 no?

It’s there in my repo. Here it is:

defmodule Hr.Repo do
  use AshPostgres.Repo,
    otp_app: :hr

  def installed_extensions do
    # Add extensions here, and the
    # migration generator will install them.
    ["ash-functions", "uuid-ossp", "citext"]
  end

  def min_pg_version do
    # Adjust this according to your postgres version
    %Version{major: 16, minor: 0, patch: 0}
  end

  @doc """
  Used by migrations --tenants to list all tenants,
  create related schemas and migrates
  """
  def all_tenants do
    for tenant <- Hr.Accounts.get_organisations!() do
      tenant.domain
    end
  end
end

Did you generate the migration for it?

For example, here is mine adding that function:

defmodule Core.Repo.Migrations.InstallAshFunctionsExtension420240925162123 do
  @moduledoc """
  Installs any extensions that are mentioned in the repo's `installed_extensions/0` callback

  This file was autogenerated with `mix ash_postgres.generate_migrations`
  """

  use Ecto.Migration

  def up do
    execute("""
    CREATE OR REPLACE FUNCTION uuid_generate_v7()
    RETURNS UUID
    AS $$
    DECLARE
      timestamp    TIMESTAMPTZ;
      microseconds INT;
    BEGIN
      timestamp    = clock_timestamp();
      microseconds = (cast(extract(microseconds FROM timestamp)::INT - (floor(extract(milliseconds FROM timestamp))::INT * 1000) AS DOUBLE PRECISION) * 4.096)::INT;

      RETURN encode(
        set_byte(
          set_byte(
            overlay(uuid_send(gen_random_uuid()) placing substring(int8send(floor(extract(epoch FROM timestamp) * 1000)::BIGINT) FROM 3) FROM 1 FOR 6
          ),
          6, (b'0111' || (microseconds >> 8)::bit(4))::bit(8)::int
        ),
        7, microseconds::bit(8)::int
      ),
      'hex')::UUID;
    END
    $$
    LANGUAGE PLPGSQL
    VOLATILE;
    """)

    execute("""
    CREATE OR REPLACE FUNCTION timestamp_from_uuid_v7(_uuid uuid)
    RETURNS TIMESTAMP WITHOUT TIME ZONE
    AS $$
      SELECT to_timestamp(('x0000' || substr(_uuid::TEXT, 1, 8) || substr(_uuid::TEXT, 10, 4))::BIT(64)::BIGINT::NUMERIC / 1000);
    $$
    LANGUAGE SQL
    IMMUTABLE PARALLEL SAFE STRICT;
    """)
  end

  def down do
    # Uncomment this if you actually want to uninstall the extensions
    # when this migration is rolled back:
    execute("DROP FUNCTION IF EXISTS uuid_generate_v7(), timestamp_from_uuid_v7(uuid)")
  end
end
1 Like

Uh, I can see. For me running mix ash_postgres.generate_migrations isn’t generating this Migration. I will copy and use yours.

I had to delete hr/priv/resource_snapshots/repo/extensions.json and the extensions migration, then run mix ash_postgres.generate_migrations to have the uuid_generate_v7() added to the migrations.

Thanks.

1 Like