]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Make cast functions to type money error safe
authorPeter Eisentraut <peter@eisentraut.org>
Mon, 30 Mar 2026 08:05:22 +0000 (10:05 +0200)
committerPeter Eisentraut <peter@eisentraut.org>
Mon, 30 Mar 2026 08:10:56 +0000 (10:10 +0200)
This converts the cast functions from types integer, bigint, and
numeric to type money to support soft errors.

Note: Casting from type money to type numeric (the other way, function
cash_numeric) is not yet error safe.

Author: jian he <jian.universality@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/CADkLM%3Dfv1JfY4Ufa-jcwwNbjQixNViskQ8jZu3Tz_p656i_4hQ%40mail.gmail.com

src/backend/utils/adt/cash.c

index 623f6eec0565af6d5e795462027380053fa76d94..f0487a60f0010ad778f3fc397739f3510417b3b3 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "common/int.h"
 #include "libpq/pqformat.h"
+#include "nodes/miscnodes.h"
 #include "utils/builtins.h"
 #include "utils/cash.h"
 #include "utils/float.h"
@@ -1106,12 +1107,12 @@ cash_numeric(PG_FUNCTION_ARGS)
 Datum
 numeric_cash(PG_FUNCTION_ARGS)
 {
-       Datum           amount = PG_GETARG_DATUM(0);
+       Numeric         amount = PG_GETARG_NUMERIC(0);
        Cash            result;
        int                     fpoint;
        int64           scale;
        int                     i;
-       Datum           numeric_scale;
+       Numeric         numeric_scale;
        struct lconv *lconvert = PGLC_localeconv();
 
        /* see comments about frac_digits in cash_in() */
@@ -1125,11 +1126,16 @@ numeric_cash(PG_FUNCTION_ARGS)
                scale *= 10;
 
        /* multiply the input amount by scale factor */
-       numeric_scale = NumericGetDatum(int64_to_numeric(scale));
-       amount = DirectFunctionCall2(numeric_mul, amount, numeric_scale);
+       numeric_scale = int64_to_numeric(scale);
+
+       amount = numeric_mul_safe(amount, numeric_scale, fcinfo->context);
+       if (unlikely(SOFT_ERROR_OCCURRED(fcinfo->context)))
+               PG_RETURN_NULL();
 
        /* note that numeric_int8 will round to nearest integer for us */
-       result = DatumGetInt64(DirectFunctionCall1(numeric_int8, amount));
+       result = numeric_int8_safe(amount, fcinfo->context);
+       if (unlikely(SOFT_ERROR_OCCURRED(fcinfo->context)))
+               PG_RETURN_NULL();
 
        PG_RETURN_CASH(result);
 }
@@ -1158,8 +1164,10 @@ int4_cash(PG_FUNCTION_ARGS)
                scale *= 10;
 
        /* compute amount * scale, checking for overflow */
-       result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
-                                                                                          Int64GetDatum(scale)));
+       if (unlikely(pg_mul_s64_overflow(amount, scale, &result)))
+               ereturn(fcinfo->context, (Datum) 0,
+                               errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                               errmsg("bigint out of range"));
 
        PG_RETURN_CASH(result);
 }
@@ -1188,8 +1196,10 @@ int8_cash(PG_FUNCTION_ARGS)
                scale *= 10;
 
        /* compute amount * scale, checking for overflow */
-       result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
-                                                                                          Int64GetDatum(scale)));
+       if (unlikely(pg_mul_s64_overflow(amount, scale, &result)))
+               ereturn(fcinfo->context, (Datum) 0,
+                               errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                               errmsg("bigint out of range"));
 
        PG_RETURN_CASH(result);
 }