Skip to content

Commit

Permalink
Implement modulo operator for MONEY/SMALLMONEY types
Browse files Browse the repository at this point in the history
Description
-----------
Due to the missing modulo operator for MONEY/SMALLMONEY types, it will choose
the other Postgres internal alternatives like int4mod and numeric_mod, which will
involve additional implicit CASTing between MONEY/SMALLMONEY types and other types.
These CASTings could cause loss of precision and wrong results. What's more, the result
of calculation won't be MONEY/SMALLMONEY types any more.

Fix
---
Implement the modulo function and operator for MONEY/SMALLMONEY types. The reason why we don't
need to implement the functions between MONEY/SMALLMONEY types and interger types is that it
will choose the MONEY-MONEY/SMALLMONEY-SMALLMONEY functions to calculate. And the result type is
expected.

Task: BABEL-5480
Signed-off-by: Bo Li <[email protected]>
  • Loading branch information
hopebo committed Jan 13, 2025
1 parent 2639702 commit 17ed68c
Show file tree
Hide file tree
Showing 5 changed files with 460 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,20 @@ ALTER OPERATOR FAMILY fixeddecimal_ops USING hash ADD
CREATE DOMAIN sys.MONEY as sys.FIXEDDECIMAL CHECK (VALUE >= -922337203685477.5808 AND VALUE <= 922337203685477.5807);
CREATE DOMAIN sys.SMALLMONEY as sys.FIXEDDECIMAL CHECK (VALUE >= -214748.3648 AND VALUE <= 214748.3647);

-- Define modulo operator directly on MONEY type.
-- Otherwise the operator between Integer and SMALLMONEY will tend to choose the fixeddecimal version,
-- which will return the result in MONEY type.
CREATE FUNCTION sys.fixeddecimalmod(sys.MONEY, sys.MONEY)
RETURNS sys.MONEY
AS 'babelfishpg_money', 'fixeddecimalmod'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;

CREATE OPERATOR sys.% (
LEFTARG = sys.MONEY,
RIGHTARG = sys.MONEY,
PROCEDURE = fixeddecimalmod
);

--
-- Cross type operators with int8
--
Expand Down Expand Up @@ -1645,6 +1659,11 @@ RETURNS sys.SMALLMONEY
AS 'babelfishpg_money', 'fixeddecimaldiv'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;

CREATE FUNCTION sys.fixeddecimalmod(sys.SMALLMONEY, sys.SMALLMONEY)
RETURNS sys.SMALLMONEY
AS 'babelfishpg_money', 'fixeddecimalmod'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;

CREATE OPERATOR sys.+ (
LEFTARG = sys.SMALLMONEY,
RIGHTARG = sys.SMALLMONEY,
Expand Down Expand Up @@ -1676,6 +1695,12 @@ CREATE OPERATOR sys./ (
PROCEDURE = fixeddecimaldiv
);

CREATE OPERATOR sys.% (
LEFTARG = sys.SMALLMONEY,
RIGHTARG = sys.SMALLMONEY,
PROCEDURE = fixeddecimalmod
);

CREATE FUNCTION sys.fixeddecimalint8pl(sys.SMALLMONEY, INT8)
RETURNS sys.SMALLMONEY
AS 'babelfishpg_money', 'fixeddecimalint8pl'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,31 @@ SELECT set_config('search_path', 'sys, '||current_setting('search_path'), false)
* So make sure that any SQL statement (DDL/DML) being added here can be executed multiple times without affecting
* final behaviour.
*/
CREATE OR REPLACE FUNCTION sys.fixeddecimalmod(sys.MONEY, sys.MONEY)
RETURNS sys.MONEY
AS 'babelfishpg_money', 'fixeddecimalmod'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;

DROP OPERATOR IF EXISTS sys.% (sys.MONEY, sys.MONEY);

CREATE OPERATOR sys.% (
LEFTARG = sys.MONEY,
RIGHTARG = sys.MONEY,
PROCEDURE = fixeddecimalmod
);

CREATE OR REPLACE FUNCTION sys.fixeddecimalmod(sys.SMALLMONEY, sys.SMALLMONEY)
RETURNS sys.SMALLMONEY
AS 'babelfishpg_money', 'fixeddecimalmod'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;

DROP OPERATOR IF EXISTS sys.% (sys.SMALLMONEY, sys.SMALLMONEY);

CREATE OPERATOR sys.% (
LEFTARG = sys.SMALLMONEY,
RIGHTARG = sys.SMALLMONEY,
PROCEDURE = fixeddecimalmod
);

-- Reset search_path to not affect any subsequent scripts
SELECT set_config('search_path', trim(leading 'sys, ' from current_setting('search_path')), false);
27 changes: 27 additions & 0 deletions contrib/babelfishpg_money/fixeddecimal.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ PG_FUNCTION_INFO_V1(fixeddecimalpl);
PG_FUNCTION_INFO_V1(fixeddecimalmi);
PG_FUNCTION_INFO_V1(fixeddecimalmul);
PG_FUNCTION_INFO_V1(fixeddecimaldiv);
PG_FUNCTION_INFO_V1(fixeddecimalmod);
PG_FUNCTION_INFO_V1(fixeddecimalabs);
PG_FUNCTION_INFO_V1(fixeddecimallarger);
PG_FUNCTION_INFO_V1(fixeddecimalsmaller);
Expand Down Expand Up @@ -1750,6 +1751,32 @@ fixeddecimaldiv(PG_FUNCTION_ARGS)
PG_RETURN_INT64((int64) result);
}

Datum
fixeddecimalmod(PG_FUNCTION_ARGS)
{
int64 dividend = PG_GETARG_INT64(0);
int64 divisor = PG_GETARG_INT64(1);

if (divisor == 0)
{
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
/* ensure compiler realizes we mustn't reach the division (gcc bug) */
PG_RETURN_NULL();
}

/*
» * Some machines throw a floating-point exception for INT_MIN % -1, which
» * is a bit silly since the correct answer is perfectly well-defined,
» * namely zero. Refer to function int4mod in Postgres.
» */
if (divisor == -1)
PG_RETURN_INT64(0);

PG_RETURN_INT64(dividend % divisor);
}

/* fixeddecimalabs()
* Absolute value
*/
Expand Down
Loading

0 comments on commit 17ed68c

Please sign in to comment.