mirror of
https://github.com/ash-project/ash_postgres.git
synced 2024-09-19 13:03:14 +12:00
improvement: add support for :uuid_v7
type (#333)
This commit is contained in:
parent
edb24ef923
commit
72b029be21
5 changed files with 141 additions and 8 deletions
|
@ -1,5 +1,5 @@
|
|||
defmodule AshPostgres.MigrationGenerator.AshFunctions do
|
||||
@latest_version 3
|
||||
@latest_version 4
|
||||
|
||||
def latest_version, do: @latest_version
|
||||
|
||||
|
@ -66,6 +66,8 @@ defmodule AshPostgres.MigrationGenerator.AshFunctions do
|
|||
\"\"\")
|
||||
|
||||
#{ash_raise_error()}
|
||||
|
||||
#{uuid_generate_v7()}
|
||||
"""
|
||||
end
|
||||
|
||||
|
@ -89,6 +91,8 @@ defmodule AshPostgres.MigrationGenerator.AshFunctions do
|
|||
|
||||
#{ash_raise_error()}
|
||||
|
||||
#{uuid_generate_v7()}
|
||||
|
||||
execute(\"\"\"
|
||||
CREATE OR REPLACE FUNCTION ash_trim_whitespace(arr text[])
|
||||
RETURNS text[] AS $$
|
||||
|
@ -117,27 +121,47 @@ defmodule AshPostgres.MigrationGenerator.AshFunctions do
|
|||
end
|
||||
|
||||
def install(1) do
|
||||
ash_raise_error()
|
||||
"""
|
||||
#{ash_raise_error()}
|
||||
|
||||
#{uuid_generate_v7()}
|
||||
"""
|
||||
end
|
||||
|
||||
def install(2) do
|
||||
ash_raise_error()
|
||||
"""
|
||||
#{ash_raise_error()}
|
||||
|
||||
#{uuid_generate_v7()}
|
||||
"""
|
||||
end
|
||||
|
||||
def install(3) do
|
||||
uuid_generate_v7()
|
||||
end
|
||||
|
||||
def drop(3) do
|
||||
"execute(\"DROP FUNCTION IF EXISTS uuid_generate_v7(), timestamp_from_uuid_v7(uuid)\")"
|
||||
end
|
||||
|
||||
def drop(2) do
|
||||
ash_raise_error(false)
|
||||
"""
|
||||
#{ash_raise_error()}
|
||||
|
||||
"execute(\"DROP FUNCTION IF EXISTS uuid_generate_v7(), timestamp_from_uuid_v7(uuid)\")"
|
||||
"""
|
||||
end
|
||||
|
||||
def drop(1) do
|
||||
"execute(\"DROP FUNCTION IF EXISTS ash_raise_error(jsonb), ash_raise_error(jsonb, ANYCOMPATIBLE)\")"
|
||||
"execute(\"DROP FUNCTION IF EXISTS uuid_generate_v7(), timestamp_from_uuid_v7(uuid), ash_raise_error(jsonb), ash_raise_error(jsonb, ANYCOMPATIBLE)\")"
|
||||
end
|
||||
|
||||
def drop(0) do
|
||||
"execute(\"DROP FUNCTION IF EXISTS ash_raise_error(jsonb), ash_raise_error(jsonb, ANYCOMPATIBLE), ash_trim_whitespace(text[])\")"
|
||||
"execute(\"DROP FUNCTION IF EXISTS uuid_generate_v7(), timestamp_from_uuid_v7(uuid), ash_raise_error(jsonb), ash_raise_error(jsonb, ANYCOMPATIBLE), ash_trim_whitespace(text[])\")"
|
||||
end
|
||||
|
||||
def drop(nil) do
|
||||
"execute(\"DROP FUNCTION IF EXISTS ash_raise_error(jsonb), ash_raise_error(jsonb, ANYCOMPATIBLE), ash_elixir_and(BOOLEAN, ANYCOMPATIBLE), ash_elixir_and(ANYCOMPATIBLE, ANYCOMPATIBLE), ash_elixir_or(ANYCOMPATIBLE, ANYCOMPATIBLE), ash_elixir_or(BOOLEAN, ANYCOMPATIBLE), ash_trim_whitespace(text[])\")"
|
||||
"execute(\"DROP FUNCTION IF EXISTS uuid_generate_v7(), timestamp_from_uuid_v7(uuid), ash_raise_error(jsonb), ash_raise_error(jsonb, ANYCOMPATIBLE), ash_elixir_and(BOOLEAN, ANYCOMPATIBLE), ash_elixir_and(ANYCOMPATIBLE, ANYCOMPATIBLE), ash_elixir_or(ANYCOMPATIBLE, ANYCOMPATIBLE), ash_elixir_or(BOOLEAN, ANYCOMPATIBLE), ash_trim_whitespace(text[])\")"
|
||||
end
|
||||
|
||||
defp ash_raise_error(prefix? \\ true) do
|
||||
|
@ -174,4 +198,45 @@ defmodule AshPostgres.MigrationGenerator.AshFunctions do
|
|||
\"\"\")
|
||||
"""
|
||||
end
|
||||
|
||||
defp uuid_generate_v7 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 LEAKPROOF;
|
||||
\"\"\")
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2848,6 +2848,7 @@ defmodule AshPostgres.MigrationGenerator do
|
|||
|
||||
defp migration_type(Ash.Type.CiString, _), do: :citext
|
||||
defp migration_type(Ash.Type.UUID, _), do: :uuid
|
||||
defp migration_type(Ash.Type.UUIDv7, _), do: :uuid
|
||||
defp migration_type(Ash.Type.Integer, _), do: :bigint
|
||||
|
||||
defp migration_type(other, constraints) do
|
||||
|
@ -2938,6 +2939,9 @@ defmodule AshPostgres.MigrationGenerator do
|
|||
default in @uuid_functions ->
|
||||
~S[fragment("gen_random_uuid()")]
|
||||
|
||||
default == (&Ash.UUIDv7.generate/0) ->
|
||||
~S[fragment("uuid_generate_v7()")]
|
||||
|
||||
default == (&DateTime.utc_now/0) ->
|
||||
~S[fragment("(now() AT TIME ZONE 'utc')")]
|
||||
|
||||
|
|
|
@ -6,5 +6,5 @@
|
|||
"citext",
|
||||
"demo-functions_v1"
|
||||
],
|
||||
"ash_functions_version": 3
|
||||
"ash_functions_version": 4
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
defmodule AshPostgres.TestRepo.Migrations.InstallAshFunctionsExtension420240622192713 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 LEAKPROOF;
|
||||
""")
|
||||
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
|
|
@ -97,6 +97,7 @@ defmodule AshPostgres.MigrationGeneratorTest do
|
|||
|
||||
attributes do
|
||||
uuid_primary_key(:id)
|
||||
uuid_v7_primary_key(:other_id)
|
||||
attribute(:title, :string, public?: true)
|
||||
attribute(:second_title, :string, public?: true)
|
||||
attribute(:title_with_source, :string, source: :t_w_s, public?: true)
|
||||
|
@ -141,6 +142,10 @@ defmodule AshPostgres.MigrationGeneratorTest do
|
|||
assert file_contents =~
|
||||
~S[add :id, :uuid, null: false, default: fragment("gen_random_uuid()"), primary_key: true]
|
||||
|
||||
# the migration adds the other_id, with its default
|
||||
assert file_contents =~
|
||||
~S[add :other_id, :uuid, null: false, default: fragment("uuid_generate_v7()"), primary_key: true]
|
||||
|
||||
# the migration adds the id, with its default
|
||||
assert file_contents =~
|
||||
~S[add :title_with_default, :text, default: "fred"]
|
||||
|
@ -195,6 +200,7 @@ defmodule AshPostgres.MigrationGeneratorTest do
|
|||
|
||||
attributes do
|
||||
uuid_primary_key(:id)
|
||||
uuid_v7_primary_key(:other_id)
|
||||
attribute(:title, :string, public?: true)
|
||||
attribute(:second_title, :string, public?: true)
|
||||
end
|
||||
|
@ -247,6 +253,10 @@ defmodule AshPostgres.MigrationGeneratorTest do
|
|||
assert file_contents =~
|
||||
~S[add :id, :uuid, null: false, default: fragment("gen_random_uuid()"), primary_key: true]
|
||||
|
||||
# the migration adds the other_id, with its default
|
||||
assert file_contents =~
|
||||
~S[add :other_id, :uuid, null: false, default: fragment("uuid_generate_v7()"), primary_key: true]
|
||||
|
||||
# the migration adds other attributes
|
||||
assert file_contents =~ ~S[add :title, :text]
|
||||
|
||||
|
|
Loading…
Reference in a new issue