Skip to content

Commit

Permalink
improvement: add comparison operators to the extension
Browse files Browse the repository at this point in the history
improvement: add more operator overloads
  • Loading branch information
zachdaniel committed Jun 18, 2024
1 parent 019f180 commit 5c6ea28
Show file tree
Hide file tree
Showing 4 changed files with 412 additions and 4 deletions.
325 changes: 324 additions & 1 deletion lib/ash_money/ash_postgres_extension.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,34 @@ if Code.ensure_loaded?(AshPostgres.CustomExtension) do
@moduledoc """
Installs the `money_with_currency` type and operators/functions for Postgres.
"""
use AshPostgres.CustomExtension, name: :ash_money, latest_version: 3
use AshPostgres.CustomExtension, name: :ash_money, latest_version: 4

def install(3) do
"""
#{Money.DDL.execute_each(add_money_greater_than())}
#{Money.DDL.execute_each(add_money_greater_than_or_equal())}
#{Money.DDL.execute_each(add_money_less_than())}
#{Money.DDL.execute_each(add_money_less_than_or_equal())}
"""
end

def install(2) do
"""
#{Money.DDL.execute_each(add_money_greater_than())}
#{Money.DDL.execute_each(add_money_greater_than_or_equal())}
#{Money.DDL.execute_each(add_money_less_than())}
#{Money.DDL.execute_each(add_money_less_than_or_equal())}
#{Money.DDL.execute_each(add_money_sub())}
#{Money.DDL.execute_each(add_money_neg())}
"""
end

def install(1) do
"""
#{Money.DDL.execute_each(add_money_greater_than())}
#{Money.DDL.execute_each(add_money_greater_than_or_equal())}
#{Money.DDL.execute_each(add_money_less_than())}
#{Money.DDL.execute_each(add_money_less_than_or_equal())}
#{Money.DDL.execute_each(add_money_sub())}
#{Money.DDL.execute_each(add_money_mult())}
#{Money.DDL.execute_each(add_money_neg())}
Expand All @@ -22,6 +39,10 @@ if Code.ensure_loaded?(AshPostgres.CustomExtension) do

def install(0) do
"""
#{Money.DDL.execute_each(add_money_greater_than())}
#{Money.DDL.execute_each(add_money_greater_than_or_equal())}
#{Money.DDL.execute_each(add_money_less_than())}
#{Money.DDL.execute_each(add_money_less_than_or_equal())}
#{Money.DDL.execute_each(Money.DDL.create_money_with_currency())}
#{Money.DDL.execute_each(add_money_sub())}
#{Money.DDL.execute_each(add_money_neg())}
Expand All @@ -32,6 +53,16 @@ if Code.ensure_loaded?(AshPostgres.CustomExtension) do
"""
end

def uninstall(4) do
"""
#{Money.DDL.execute_each(remove_money_greater_than())}
#{Money.DDL.execute_each(remove_money_greater_than_or_equal())}
#{Money.DDL.execute_each(remove_money_less_than())}
#{Money.DDL.execute_each(remove_money_less_than_or_equal())}
#{uninstall(3)}
"""
end

def uninstall(3) do
"""
#{Money.DDL.execute_each(remove_money_sub())}
Expand All @@ -50,6 +81,298 @@ if Code.ensure_loaded?(AshPostgres.CustomExtension) do
"""
end

defp add_money_greater_than do
"""
CREATE OR REPLACE FUNCTION money_gt(money_1 money_with_currency, money_2 money_with_currency)
RETURNS BOOLEAN
IMMUTABLE
STRICT
LANGUAGE plpgsql
AS $$
DECLARE
currency varchar;
result boolean;
BEGIN
IF currency_code(money_1) = currency_code(money_2) THEN
currency := currency_code(money_1);
result := amount(money_1) > amount(money_2);
return result;
ELSE
RAISE EXCEPTION
'Incompatible currency codes for > operator. Expected both currency codes to be %', currency_code(money_1)
USING HINT = 'Please ensure both columns have the same currency code',
ERRCODE = '22033';
END IF;
END;
$$;
CREATE OR REPLACE FUNCTION money_gt(money_1 money_with_currency, amount numeric)
RETURNS BOOLEAN
IMMUTABLE
STRICT
LANGUAGE plpgsql
AS $$
DECLARE
currency varchar;
result boolean;
BEGIN
currency := currency_code(money_1);
result := amount(money_1) > amount;
return result;
END;
$$;
CREATE OPERATOR > (
leftarg = money_with_currency,
rightarg = money_with_currency,
procedure = money_gt
);
CREATE OPERATOR > (
leftarg = money_with_currency,
rightarg = numeric,
procedure = money_gt
);
"""
end

defp add_money_greater_than_or_equal do
"""
CREATE OR REPLACE FUNCTION money_gte(money_1 money_with_currency, money_2 money_with_currency)
RETURNS BOOLEAN
IMMUTABLE
STRICT
LANGUAGE plpgsql
AS $$
DECLARE
currency varchar;
result boolean;
BEGIN
IF currency_code(money_1) = currency_code(money_2) THEN
currency := currency_code(money_1);
result := amount(money_1) >= amount(money_2);
return result;
ELSE
RAISE EXCEPTION
'Incompatible currency codes for >= operator. Expected both currency codes to be %', currency_code(money_1)
USING HINT = 'Please ensure both columns have the same currency code',
ERRCODE = '22033';
END IF;
END;
$$;
CREATE OR REPLACE FUNCTION money_gte(money_1 money_with_currency, amount numeric)
RETURNS BOOLEAN
IMMUTABLE
STRICT
LANGUAGE plpgsql
AS $$
DECLARE
currency varchar;
result boolean;
BEGIN
currency := currency_code(money_1);
result := amount(money_1) >= amount;
return result;
END;
$$;
CREATE OPERATOR >= (
leftarg = money_with_currency,
rightarg = money_with_currency,
procedure = money_gt
);
CREATE OPERATOR >= (
leftarg = money_with_currency,
rightarg = numeric,
procedure = money_gt
);
"""
end

defp add_money_less_than do
"""
CREATE OR REPLACE FUNCTION money_lt(money_1 money_with_currency, money_2 money_with_currency)
RETURNS BOOLEAN
IMMUTABLE
STRICT
LANGUAGE plpgsql
AS $$
DECLARE
currency varchar;
result boolean;
BEGIN
IF currency_code(money_1) = currency_code(money_2) THEN
currency := currency_code(money_1);
result := amount(money_1) < amount(money_2);
return result;
ELSE
RAISE EXCEPTION
'Incompatible currency codes for < operator. Expected both currency codes to be %', currency_code(money_1)
USING HINT = 'Please ensure both columns have the same currency code',
ERRCODE = '22033';
END IF;
END;
$$;
CREATE OR REPLACE FUNCTION money_lt(money_1 money_with_currency, amount numeric)
RETURNS BOOLEAN
IMMUTABLE
STRICT
LANGUAGE plpgsql
AS $$
DECLARE
currency varchar;
result boolean;
BEGIN
currency := currency_code(money_1);
result := amount(money_1) < amount;
return result;
END;
$$;
CREATE OPERATOR < (
leftarg = money_with_currency,
rightarg = money_with_currency,
procedure = money_lt
);
CREATE OPERATOR < (
leftarg = money_with_currency,
rightarg = numeric,
procedure = money_lt
);
"""
end

defp add_money_less_than_or_equal do
"""
CREATE OR REPLACE FUNCTION money_lte(money_1 money_with_currency, money_2 money_with_currency)
RETURNS BOOLEAN
IMMUTABLE
STRICT
LANGUAGE plpgsql
AS $$
DECLARE
currency varchar;
result boolean;
BEGIN
IF currency_code(money_1) = currency_code(money_2) THEN
currency := currency_code(money_1);
result := amount(money_1) <= amount(money_2);
return result;
ELSE
RAISE EXCEPTION
'Incompatible currency codes for <= operator. Expected both currency codes to be %', currency_code(money_1)
USING HINT = 'Please ensure both columns have the same currency code',
ERRCODE = '22033';
END IF;
END;
$$;
CREATE OR REPLACE FUNCTION money_lte(money_1 money_with_currency, amount numeric)
RETURNS BOOLEAN
IMMUTABLE
STRICT
LANGUAGE plpgsql
AS $$
DECLARE
currency varchar;
result boolean;
BEGIN
currency := currency_code(money_1);
result := amount(money_1) <= amount;
return result;
END;
$$;
CREATE OPERATOR <= (
leftarg = money_with_currency,
rightarg = money_with_currency,
procedure = money_lte
);
CREATE OPERATOR <= (
leftarg = money_with_currency,
rightarg = numeric,
procedure = money_lte
);
"""
end

defp remove_money_greater_than do
"""
DROP OPERATOR >(money_with_currency, money_with_currency);
DROP OPERATOR >(money_with_currency, numeric);
DROP FUNCTION IF EXISTS money_gt(money_1 money_with_currency, money_2 money_with_currency);
DROP FUNCTION IF EXISTS money_gt(money_1 money_with_currency, amount numeric);
"""
end

defp remove_money_greater_than_or_equal do
"""
DROP OPERATOR >=(money_with_currency, money_with_currency);
DROP OPERATOR >=(money_with_currency, numeric);
DROP FUNCTION IF EXISTS money_gte(money_1 money_with_currency, money_2 money_with_currency);
DROP FUNCTION IF EXISTS money_gte(money_1 money_with_currency, amount numeric);
"""
end

defp remove_money_less_than do
"""
DROP OPERATOR <(money_with_currency, money_with_currency);
DROP OPERATOR <(money_with_currency, numeric);
DROP FUNCTION IF EXISTS money_lt(money_1 money_with_currency, money_2 money_with_currency);
DROP FUNCTION IF EXISTS money_lt(money_1 money_with_currency, amount numeric);
"""
end

defp remove_money_less_than_or_equal do
"""
DROP OPERATOR <=(money_with_currency, money_with_currency);
DROP OPERATOR <=(money_with_currency, numeric);
DROP FUNCTION IF EXISTS money_lte(money_1 money_with_currency, money_2 money_with_currency);
DROP FUNCTION IF EXISTS money_lte(money_1 money_with_currency, amount numeric);
"""
end

defp add_money_neg do
"""
CREATE OR REPLACE FUNCTION money_neg(money_1 money_with_currency)
Expand Down
Loading

0 comments on commit 5c6ea28

Please sign in to comment.