Skip to content

Commit b82791c

Browse files
Add overflow checks to money type.
None of the arithmetic functions for the the money type handle overflow. This commit introduces several helper functions with overflow checking and makes use of them in the money type's arithmetic functions. Fixes bug #18240. Reported-by: Alexander Lakhin Author: Joseph Koshakow Discussion: https://postgr.es/m/18240-c5da758d7dc1ecf0%40postgresql.org Discussion: https://postgr.es/m/CAAvxfHdBPOyEGS7s%2Bxf4iaW0-cgiq25jpYdWBqQqvLtLe_t6tw%40mail.gmail.com Backpatch-through: 12
1 parent e2e9928 commit b82791c

File tree

3 files changed

+124
-80
lines changed

3 files changed

+124
-80
lines changed

src/backend/utils/adt/cash.c

Lines changed: 94 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "libpq/pqformat.h"
2727
#include "utils/builtins.h"
2828
#include "utils/cash.h"
29+
#include "utils/float.h"
2930
#include "utils/numeric.h"
3031
#include "utils/pg_locale.h"
3132

@@ -86,6 +87,82 @@ num_word(Cash value)
8687
return buf;
8788
} /* num_word() */
8889

90+
static inline Cash
91+
cash_pl_cash(Cash c1, Cash c2)
92+
{
93+
Cash res;
94+
95+
if (unlikely(pg_add_s64_overflow(c1, c2, &res)))
96+
ereport(ERROR,
97+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
98+
errmsg("money out of range")));
99+
100+
return res;
101+
}
102+
103+
static inline Cash
104+
cash_mi_cash(Cash c1, Cash c2)
105+
{
106+
Cash res;
107+
108+
if (unlikely(pg_sub_s64_overflow(c1, c2, &res)))
109+
ereport(ERROR,
110+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
111+
errmsg("money out of range")));
112+
113+
return res;
114+
}
115+
116+
static inline Cash
117+
cash_mul_float8(Cash c, float8 f)
118+
{
119+
float8 res = rint(float8_mul((float8) c, f));
120+
121+
if (unlikely(isnan(res) || !FLOAT8_FITS_IN_INT64(res)))
122+
ereport(ERROR,
123+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
124+
errmsg("money out of range")));
125+
126+
return (Cash) res;
127+
}
128+
129+
static inline Cash
130+
cash_div_float8(Cash c, float8 f)
131+
{
132+
float8 res = rint(float8_div((float8) c, f));
133+
134+
if (unlikely(isnan(res) || !FLOAT8_FITS_IN_INT64(res)))
135+
ereport(ERROR,
136+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
137+
errmsg("money out of range")));
138+
139+
return (Cash) res;
140+
}
141+
142+
static inline Cash
143+
cash_mul_int64(Cash c, int64 i)
144+
{
145+
Cash res;
146+
147+
if (unlikely(pg_mul_s64_overflow(c, i, &res)))
148+
ereport(ERROR,
149+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
150+
errmsg("money out of range")));
151+
152+
return res;
153+
}
154+
155+
static inline Cash
156+
cash_div_int64(Cash c, int64 i)
157+
{
158+
if (unlikely(i == 0))
159+
ereport(ERROR,
160+
(errcode(ERRCODE_DIVISION_BY_ZERO),
161+
errmsg("division by zero")));
162+
163+
return c / i;
164+
}
165+
89166
/* cash_in()
90167
* Convert a string to a cash data type.
91168
* Format is [$]###[,]###[.##]
@@ -611,11 +688,8 @@ cash_pl(PG_FUNCTION_ARGS)
611688
{
612689
Cash c1 = PG_GETARG_CASH(0);
613690
Cash c2 = PG_GETARG_CASH(1);
614-
Cash result;
615-
616-
result = c1 + c2;
617691

618-
PG_RETURN_CASH(result);
692+
PG_RETURN_CASH(cash_pl_cash(c1, c2));
619693
}
620694

621695

@@ -627,11 +701,8 @@ cash_mi(PG_FUNCTION_ARGS)
627701
{
628702
Cash c1 = PG_GETARG_CASH(0);
629703
Cash c2 = PG_GETARG_CASH(1);
630-
Cash result;
631-
632-
result = c1 - c2;
633704

634-
PG_RETURN_CASH(result);
705+
PG_RETURN_CASH(cash_mi_cash(c1, c2));
635706
}
636707

637708

@@ -663,10 +734,8 @@ cash_mul_flt8(PG_FUNCTION_ARGS)
663734
{
664735
Cash c = PG_GETARG_CASH(0);
665736
float8 f = PG_GETARG_FLOAT8(1);
666-
Cash result;
667737

668-
result = rint(c * f);
669-
PG_RETURN_CASH(result);
738+
PG_RETURN_CASH(cash_mul_float8(c, f));
670739
}
671740

672741

@@ -678,10 +747,8 @@ flt8_mul_cash(PG_FUNCTION_ARGS)
678747
{
679748
float8 f = PG_GETARG_FLOAT8(0);
680749
Cash c = PG_GETARG_CASH(1);
681-
Cash result;
682750

683-
result = rint(f * c);
684-
PG_RETURN_CASH(result);
751+
PG_RETURN_CASH(cash_mul_float8(c, f));
685752
}
686753

687754

@@ -693,15 +760,8 @@ cash_div_flt8(PG_FUNCTION_ARGS)
693760
{
694761
Cash c = PG_GETARG_CASH(0);
695762
float8 f = PG_GETARG_FLOAT8(1);
696-
Cash result;
697763

698-
if (f == 0.0)
699-
ereport(ERROR,
700-
(errcode(ERRCODE_DIVISION_BY_ZERO),
701-
errmsg("division by zero")));
702-
703-
result = rint(c / f);
704-
PG_RETURN_CASH(result);
764+
PG_RETURN_CASH(cash_div_float8(c, f));
705765
}
706766

707767

@@ -713,10 +773,8 @@ cash_mul_flt4(PG_FUNCTION_ARGS)
713773
{
714774
Cash c = PG_GETARG_CASH(0);
715775
float4 f = PG_GETARG_FLOAT4(1);
716-
Cash result;
717776

718-
result = rint(c * (float8) f);
719-
PG_RETURN_CASH(result);
777+
PG_RETURN_CASH(cash_mul_float8(c, (float8) f));
720778
}
721779

722780

@@ -728,10 +786,8 @@ flt4_mul_cash(PG_FUNCTION_ARGS)
728786
{
729787
float4 f = PG_GETARG_FLOAT4(0);
730788
Cash c = PG_GETARG_CASH(1);
731-
Cash result;
732789

733-
result = rint((float8) f * c);
734-
PG_RETURN_CASH(result);
790+
PG_RETURN_CASH(cash_mul_float8(c, (float8) f));
735791
}
736792

737793

@@ -744,15 +800,8 @@ cash_div_flt4(PG_FUNCTION_ARGS)
744800
{
745801
Cash c = PG_GETARG_CASH(0);
746802
float4 f = PG_GETARG_FLOAT4(1);
747-
Cash result;
748-
749-
if (f == 0.0)
750-
ereport(ERROR,
751-
(errcode(ERRCODE_DIVISION_BY_ZERO),
752-
errmsg("division by zero")));
753803

754-
result = rint(c / (float8) f);
755-
PG_RETURN_CASH(result);
804+
PG_RETURN_CASH(cash_div_float8(c, (float8) f));
756805
}
757806

758807

@@ -764,10 +813,8 @@ cash_mul_int8(PG_FUNCTION_ARGS)
764813
{
765814
Cash c = PG_GETARG_CASH(0);
766815
int64 i = PG_GETARG_INT64(1);
767-
Cash result;
768816

769-
result = c * i;
770-
PG_RETURN_CASH(result);
817+
PG_RETURN_CASH(cash_mul_int64(c, i));
771818
}
772819

773820

@@ -779,10 +826,8 @@ int8_mul_cash(PG_FUNCTION_ARGS)
779826
{
780827
int64 i = PG_GETARG_INT64(0);
781828
Cash c = PG_GETARG_CASH(1);
782-
Cash result;
783829

784-
result = i * c;
785-
PG_RETURN_CASH(result);
830+
PG_RETURN_CASH(cash_mul_int64(c, i));
786831
}
787832

788833
/* cash_div_int8()
@@ -793,16 +838,8 @@ cash_div_int8(PG_FUNCTION_ARGS)
793838
{
794839
Cash c = PG_GETARG_CASH(0);
795840
int64 i = PG_GETARG_INT64(1);
796-
Cash result;
797-
798-
if (i == 0)
799-
ereport(ERROR,
800-
(errcode(ERRCODE_DIVISION_BY_ZERO),
801-
errmsg("division by zero")));
802841

803-
result = c / i;
804-
805-
PG_RETURN_CASH(result);
842+
PG_RETURN_CASH(cash_div_int64(c, i));
806843
}
807844

808845

@@ -814,10 +851,8 @@ cash_mul_int4(PG_FUNCTION_ARGS)
814851
{
815852
Cash c = PG_GETARG_CASH(0);
816853
int32 i = PG_GETARG_INT32(1);
817-
Cash result;
818854

819-
result = c * i;
820-
PG_RETURN_CASH(result);
855+
PG_RETURN_CASH(cash_mul_int64(c, (int64) i));
821856
}
822857

823858

@@ -829,10 +864,8 @@ int4_mul_cash(PG_FUNCTION_ARGS)
829864
{
830865
int32 i = PG_GETARG_INT32(0);
831866
Cash c = PG_GETARG_CASH(1);
832-
Cash result;
833867

834-
result = i * c;
835-
PG_RETURN_CASH(result);
868+
PG_RETURN_CASH(cash_mul_int64(c, (int64) i));
836869
}
837870

838871

@@ -845,16 +878,8 @@ cash_div_int4(PG_FUNCTION_ARGS)
845878
{
846879
Cash c = PG_GETARG_CASH(0);
847880
int32 i = PG_GETARG_INT32(1);
848-
Cash result;
849-
850-
if (i == 0)
851-
ereport(ERROR,
852-
(errcode(ERRCODE_DIVISION_BY_ZERO),
853-
errmsg("division by zero")));
854-
855-
result = c / i;
856881

857-
PG_RETURN_CASH(result);
882+
PG_RETURN_CASH(cash_div_int64(c, (int64) i));
858883
}
859884

860885

@@ -866,10 +891,8 @@ cash_mul_int2(PG_FUNCTION_ARGS)
866891
{
867892
Cash c = PG_GETARG_CASH(0);
868893
int16 s = PG_GETARG_INT16(1);
869-
Cash result;
870894

871-
result = c * s;
872-
PG_RETURN_CASH(result);
895+
PG_RETURN_CASH(cash_mul_int64(c, (int64) s));
873896
}
874897

875898
/* int2_mul_cash()
@@ -880,10 +903,8 @@ int2_mul_cash(PG_FUNCTION_ARGS)
880903
{
881904
int16 s = PG_GETARG_INT16(0);
882905
Cash c = PG_GETARG_CASH(1);
883-
Cash result;
884906

885-
result = s * c;
886-
PG_RETURN_CASH(result);
907+
PG_RETURN_CASH(cash_mul_int64(c, (int64) s));
887908
}
888909

889910
/* cash_div_int2()
@@ -895,15 +916,8 @@ cash_div_int2(PG_FUNCTION_ARGS)
895916
{
896917
Cash c = PG_GETARG_CASH(0);
897918
int16 s = PG_GETARG_INT16(1);
898-
Cash result;
899919

900-
if (s == 0)
901-
ereport(ERROR,
902-
(errcode(ERRCODE_DIVISION_BY_ZERO),
903-
errmsg("division by zero")));
904-
905-
result = c / s;
906-
PG_RETURN_CASH(result);
920+
PG_RETURN_CASH(cash_div_int64(c, (int64) s));
907921
}
908922

909923
/* cashlarger()

src/test/regress/expected/money.out

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,3 +503,22 @@ SELECT '-92233720368547758.08'::money::numeric;
503503
-92233720368547758.08
504504
(1 row)
505505

506+
-- overflow checks
507+
SELECT '92233720368547758.07'::money + '0.01'::money;
508+
ERROR: money out of range
509+
SELECT '-92233720368547758.08'::money - '0.01'::money;
510+
ERROR: money out of range
511+
SELECT '92233720368547758.07'::money * 2::float8;
512+
ERROR: money out of range
513+
SELECT '-1'::money / 1.175494e-38::float4;
514+
ERROR: money out of range
515+
SELECT '92233720368547758.07'::money * 2::int4;
516+
ERROR: money out of range
517+
SELECT '1'::money / 0::int2;
518+
ERROR: division by zero
519+
SELECT '42'::money * 'inf'::float8;
520+
ERROR: money out of range
521+
SELECT '42'::money * '-inf'::float8;
522+
ERROR: money out of range
523+
SELECT '42'::money * 'nan'::float4;
524+
ERROR: money out of range

src/test/regress/sql/money.sql

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,14 @@ SELECT '12345678901234567'::money::numeric;
129129
SELECT '-12345678901234567'::money::numeric;
130130
SELECT '92233720368547758.07'::money::numeric;
131131
SELECT '-92233720368547758.08'::money::numeric;
132+
133+
-- overflow checks
134+
SELECT '92233720368547758.07'::money + '0.01'::money;
135+
SELECT '-92233720368547758.08'::money - '0.01'::money;
136+
SELECT '92233720368547758.07'::money * 2::float8;
137+
SELECT '-1'::money / 1.175494e-38::float4;
138+
SELECT '92233720368547758.07'::money * 2::int4;
139+
SELECT '1'::money / 0::int2;
140+
SELECT '42'::money * 'inf'::float8;
141+
SELECT '42'::money * '-inf'::float8;
142+
SELECT '42'::money * 'nan'::float4;

0 commit comments

Comments
 (0)